Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1 | .. _core: |
| 2 | |
| 3 | #### |
| 4 | Core |
| 5 | #### |
| 6 | |
| 7 | .. _interrupt_handling: |
| 8 | |
| 9 | Interrupt handling |
| 10 | ****************** |
| 11 | This section describes how :ref:`optee_os` handles switches of world execution |
Joakim Bech | 1e50686 | 2019-06-24 10:00:51 +0200 | [diff] [blame] | 12 | context based on :ref:`SMC` exceptions and interrupt notifications. Interrupt |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 13 | notifications are IRQ/FIQ exceptions which may also imply switching of world |
| 14 | execution context: normal world to secure world, or secure world to normal |
| 15 | world. |
| 16 | |
| 17 | Use cases of world context switch |
| 18 | ================================= |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 19 | This section lists all the cases where OP-TEE OS is involved in world context |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 20 | switches. Optee_os executes in the secure world. World switch is done by the |
Joakim Bech | eb39780 | 2019-09-13 11:45:06 +0200 | [diff] [blame] | 21 | core's secure monitor level/mode, referred below as the Monitor. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 22 | |
| 23 | When the normal world invokes the secure world, the normal world executes a SMC |
| 24 | instruction. The SMC exception is always trapped by the Monitor. If the related |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 25 | service targets the trusted OS, the Monitor will switch to OP-TEE OS world |
| 26 | execution. When the secure world returns to the normal world, OP-TEE OS executes |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 27 | a SMC that is caught by the Monitor which switches back to the normal world. |
| 28 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 29 | When a secure interrupt is signaled by the Arm GIC, it shall reach the OP-TEE OS |
| 30 | interrupt exception vector. If the secure world is executing, OP-TEE OS will |
| 31 | handle interrupt straight from its exception vector. If the normal world is |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 32 | executing when the secure interrupt raises, the Monitor vector must handle the |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 33 | exception and invoke OP-TEE OS to serve the interrupt. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 34 | |
| 35 | When a non-secure interrupt is signaled by the Arm GIC, it shall reach the |
| 36 | normal world interrupt exception vector. If the normal world is executing, it |
| 37 | will handle straight the exception from its exception vector. If the secure |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 38 | world is executing when the non-secure interrupt raises, OP-TEE OS will |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 39 | temporarily return back to normal world via the Monitor to let normal world |
| 40 | serve the interrupt. |
| 41 | |
| 42 | Core exception vectors |
| 43 | ====================== |
| 44 | Monitor vector is ``VBAR_EL3`` in AArch64 and ``MVBAR`` in Armv7-A/AArch32. |
| 45 | Monitor can be reached while normal world or secure world is executing. The |
| 46 | executing secure state is known to the Monitor through the ``SCR_NS``. |
| 47 | |
| 48 | Monitor can be reached from a SMC exception, an IRQ or FIQ exception (so-called |
| 49 | interrupts) and from asynchronous aborts. Obviously monitor aborts (data, |
| 50 | prefetch, undef) are local to the Monitor execution. |
| 51 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 52 | The Monitor can be external to OP-TEE OS (case ``CFG_WITH_ARM_TRUSTED_FW=y``). |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 53 | If not, provides a local secure monitor ``core/arch/arm/sm``. Armv7-A platforms |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 54 | should use the OP-TEE OS secure monitor. Armv8-A platforms are likely to rely on |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 55 | an `Trusted Firmware A`_. |
| 56 | |
| 57 | When executing outside the Monitor, the system is executing either in the |
| 58 | normal world (``SCR_NS=1``) or in the secure world (``SCR_NS=0``). Each world |
| 59 | owns its own exception vector table (state vector): |
| 60 | |
| 61 | - ``VBAR_EL2`` or ``VBAR_EL1`` non-secure or ``VBAR_EL1`` secure for |
| 62 | AArch64. |
| 63 | - ``HVBAR`` or ``VBAR`` non-secure or ``VBAR`` secure for Armv7-A and |
| 64 | AArch32. |
| 65 | |
| 66 | All SMC exceptions are trapped in the Monitor vector. IRQ/FIQ exceptions can be |
| 67 | trapped either in the Monitor vector or in the state vector of the executing |
| 68 | world. |
| 69 | |
| 70 | When the normal world is executing, the system is configured to route: |
| 71 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 72 | - secure interrupts to the Monitor that will forward to OP-TEE OS |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 73 | - non-secure interrupts to the executing world exception vector. |
| 74 | |
| 75 | When the secure world is executing, the system is configured to route: |
| 76 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 77 | - secure and non-secure interrupts to the executing OP-TEE OS exception |
| 78 | vector. OP-TEE OS shall forward the non-secure interrupts to the normal |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 79 | world. |
| 80 | |
| 81 | Optee_os non-secure interrupts are always trapped in the state vector of the |
| 82 | executing world. This is reflected by a static value of ``SCR_(IRQ|FIQ)``. |
| 83 | |
| 84 | .. _native_foreign_irqs: |
| 85 | |
| 86 | Native and foreign interrupts |
| 87 | ============================= |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 88 | Two types of interrupt are defined from OP-TEE OS point of view. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 89 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 90 | - **Native interrupt** - The interrupt handled by OP-TEE OS, secure |
| 91 | interrupts targetting S-EL1 or secure privileged mode |
| 92 | - **Foreign interrupt** - The interrupt not handled by OP-TEE OS, non-secure |
| 93 | interrupts targetting normal world or secure interrupts targetting EL3. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 94 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 95 | For Arm **GICv2** mode, a native interrupt is signalled with a FIQ and a |
| 96 | foreign interrupt is signalled with an IRQ. For Arm **GICv3** mode, a |
| 97 | foreign interrupts is signalled as a FIQ which could be handled by either |
| 98 | secure world (aarch32 Monitor mode or aarch64 EL3) or normal world. |
| 99 | |
| 100 | Arm GICv3 mode can be enabled by setting ``CFG_ARM_GICV3=y``. |
| 101 | Native interrupts must be securely routed to OP-TEE OS. Foreign interrupts, when |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 102 | trapped during secure world execution might need to be efficiently routed to |
| 103 | the normal world. |
| 104 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 105 | IRQ and FIQ keeps their meaning in normal world so for clarity we will keep |
| 106 | using those names in the normal world context. |
| 107 | |
| 108 | Normal World invokes OP-TEE OS using SMC |
| 109 | ======================================== |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 110 | |
| 111 | **Entering the Secure Monitor** |
| 112 | |
| 113 | The monitor manages all entries and exits of secure world. To enter secure |
| 114 | world from normal world the monitor saves the state of normal world (general |
| 115 | purpose registers and system registers which are not banked) and restores the |
| 116 | previous state of secure world. Then a return from exception is performed and |
| 117 | the restored secure state is resumed. Exit from secure world to normal world is |
| 118 | the reverse. |
| 119 | |
| 120 | Some general purpose registers are not saved and restored on entry and exit, |
| 121 | those are used to pass parameters between secure and normal world (see |
| 122 | ARM_DEN0028A_SMC_Calling_Convention_ for details). |
| 123 | |
| 124 | **Entry and exit of Trusted OS** |
| 125 | |
| 126 | On entry and exit of Trusted OS each CPU is uses a separate entry stack and runs |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 127 | with IRQ and FIQ masked. SMCs are categorised in two flavors: **fast** and |
| 128 | **yielding**. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 129 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 130 | - For **fast** SMCs, OP-TEE OS will execute on the entry stack with IRQ/FIQ |
| 131 | masked until the execution returns to normal world. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 132 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 133 | - For **yielding** SMCs, OP-TEE OS will at some point execute the requested |
| 134 | service with interrupts unmasked. In order to handle interrupts, mainly |
| 135 | forwarding of foreign interrupts, OP-TEE OS assigns a trusted thread |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 136 | (`core/arch/arm/kernel/thread.c`_) to the SMC request. The trusted thread |
| 137 | stores the execution context of the requested service. This context can be |
| 138 | suspended and resumed as the requested service executes and is |
| 139 | interrupted. The trusted thread is released only once the service |
| 140 | execution returns with a completion status. |
| 141 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 142 | For **yielding** SMCs, OP-TEE OS allocates or resumes a trusted thread |
| 143 | then unmasks the IRQ and FIQ lines. When the OP-TEE OS needs to invoke the |
| 144 | normal world from a foreign interrupt or a remote service call, OP-TEE OS |
| 145 | masks IRQ and FIQ and suspends the trusted thread. When suspending, |
| 146 | OP-TEE OS gets back to the entry stack. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 147 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 148 | - **Both** fast and yielding SMCs end on the entry stack with IRQ and |
| 149 | FIQ masked and OP-TEE OS invokes the Monitor through a SMC to return |
| 150 | to the normal world. |
Jens Wiklander | 2c39d74 | 2021-05-10 16:02:08 +0200 | [diff] [blame] | 151 | |
| 152 | .. uml:: |
| 153 | :align: center |
| 154 | :caption: SMC entry to secure world |
| 155 | |
| 156 | participant "Normal World" as nwd |
| 157 | participant "Secure Monitor" as smon |
| 158 | participant "OP-TEE OS entry" as entry |
| 159 | participant "OP-TEE OS" as optee |
| 160 | == IRQ and FIQ unmasked == |
| 161 | nwd -> smon : smc: TEE_FUNC_INVOKE |
| 162 | smon -> smon : Save non-secure context |
| 163 | smon -> smon : Restore secure context |
| 164 | smon --> entry : eret: TEE_FUNC_INVOKE |
| 165 | entry -> entry : assign thread |
| 166 | entry -> optee : TEE_FUNC_INVOKE |
| 167 | == IRQ and FIQ unmasked == |
| 168 | optee -> optee : process |
| 169 | == IRQ and FIQ masked == |
| 170 | optee --> entry : SMC_CALL_RETURN |
| 171 | entry -> smon : smc: SMC_CALL_RETURN |
| 172 | smon -> smon : Save secure context |
| 173 | smon -> smon : Restore non-secure context |
| 174 | == IRQ and FIQ unmasked == |
| 175 | smon --> nwd : eret: return |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 176 | |
| 177 | Deliver non-secure interrupts to Normal World |
| 178 | ============================================= |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 179 | |
| 180 | **Forward a Foreign Interrupt from Secure World to Normal World** |
| 181 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 182 | When a foreign interrupt is received in secure world as an IRQ or FIQ |
| 183 | exception then secure world: |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 184 | |
| 185 | 1. Saves trusted thread context (entire state of all processor modes for |
| 186 | Armv7-A) |
| 187 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 188 | 2. Masks all interrupts (IRQ and FIQ) |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 189 | |
| 190 | 3. Switches to entry stack |
| 191 | |
| 192 | 4. Issues an SMC with a value to indicates to normal world that an IRQ has |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 193 | been detected and last SMC call should be continued |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 194 | |
| 195 | The monitor restores normal world context with a return code indicating that an |
| 196 | IRQ is about to be delivered. Normal world issues a new SMC indicating that it |
| 197 | should continue last SMC. |
| 198 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 199 | The monitor restores secure world context which locates the previously |
| 200 | saved context and checks that it is a return from a foreign interrupt that |
| 201 | is requested before restoring the context and lets the secure world foreign |
| 202 | interrupt handler return from exception where the execution would be |
| 203 | resumed. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 204 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 205 | Note that the monitor itself does not know or care that it has just forwarded |
| 206 | a foreign interrupt to normal world. The bookkeeping is done in the trusted |
| 207 | thread handling in OP-TEE OS. Normal world is responsible to decide when |
| 208 | the secure world thread should resume execution (for details, see |
| 209 | :ref:`thread_handling`). |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 210 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 211 | .. uml:: |
| 212 | :align: center |
| 213 | :caption: Foreign interrupt received in secure world and forwarded to |
| 214 | normal world |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 215 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 216 | participant "Normal World" as nwd |
| 217 | participant "Secure Monitor" as smon |
| 218 | participant "OP-TEE OS entry" as entry |
| 219 | participant "OP-TEE OS" as optee |
| 220 | == IRQ and FIQ unmasked == |
| 221 | optee -> optee : process |
| 222 | == IRQ and FIQ unmasked,\nForeign interrupt received == |
| 223 | optee -> optee : suspend thread |
| 224 | optee -> entry : forward foreign interrupt |
| 225 | entry -> smon : smc: forward foreign interrupt |
| 226 | smon -> smon: Save secure context |
| 227 | smon -> smon: Restore non-secure context |
| 228 | == IRQ and FIQ unmasked == |
| 229 | smon --> nwd : eret: IRQ forwarded |
| 230 | == FIQ unmasked, IRQ received == |
| 231 | nwd -> nwd : process IRQ |
| 232 | == IRQ and FIQ unmasked == |
| 233 | nwd -> smon : smc: return from IRQ |
| 234 | == IRQ and FIQ masked == |
| 235 | smon -> smon : Save non-secure context |
| 236 | smon -> smon : Restore secure context |
| 237 | smon --> entry : eret: return from foreign interrupt |
| 238 | entry -> entry : find thread |
| 239 | entry --> optee : resume execution |
| 240 | == IRQ and FIQ unmasked == |
| 241 | optee -> optee : process |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 242 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 243 | **Deliver a foreign interrupt to normal world when ``SCR_NS`` is set** |
| 244 | |
| 245 | Since ``SCR_IRQ`` is cleared, an IRQ will be delivered using the exception |
| 246 | vector (``VBAR``) in the normal world. The IRQ is received as any other |
| 247 | exception by normal world, the monitor and the OP-TEE OS are not involved |
| 248 | at all. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 249 | |
| 250 | Deliver secure interrupts to Secure World |
| 251 | ========================================= |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 252 | A secure (foreign) interrupt can be received during two different states, |
| 253 | either in normal world (``SCR_NS`` is set) or in secure world (``SCR_NS`` |
| 254 | is cleared). When the secure monitor is active (Armv8-A EL3 or Armv7-A |
| 255 | Monitor mode) FIQ and IRQ are masked. FIQ reception in the two different |
| 256 | states is described below. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 257 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 258 | **Deliver secure interrupt to secure world when SCR_NS is set** |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 259 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 260 | When the monitor traps a secure interrupt it: |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 261 | |
| 262 | 1. Saves normal world context and restores secure world context from last |
| 263 | secure world exit (which will have IRQ and FIQ blocked) |
| 264 | 2. Clears ``SCR_FIQ`` when clearing ``SCR_NS`` |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 265 | 3. Does a return from exception into OP-TEE OS via the secure interrupt |
| 266 | entry point |
| 267 | 4. OP-TEE OS handles the native interrupt directly in the entry point |
| 268 | 5. OP-TEE OS issues an SMC to return to normal world |
| 269 | 6. The monitor saves the secure world context and restores the normal world context |
| 270 | 7. Does a return from exception into the restored context |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 271 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 272 | .. uml:: |
| 273 | :align: center |
| 274 | :caption: Secure interrupt received when SCR_NS is set |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 275 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 276 | participant "Normal World" as nwd |
| 277 | participant "Secure Monitor" as smon |
| 278 | participant "OP-TEE OS entry" as entry |
| 279 | participant "OP-TEE OS" as optee |
| 280 | == IRQ and FIQ unmasked == |
| 281 | == Running in non-secure world (SCR_NS set) == |
| 282 | nwd -> nwd : process |
| 283 | == IRQ and FIQ masked,\nSecure interrupt received == |
| 284 | smon -> smon : Save non-secure context |
| 285 | smon -> smon : Restore secure context |
| 286 | smon --> entry : eret: native interrupt entry point |
| 287 | entry -> entry: process received native interrupt |
| 288 | entry -> smon: smc: return |
| 289 | smon -> smon : Save secure context |
| 290 | smon -> smon : Restore non-secure context |
| 291 | smon --> nwd : eret: return to Normal world |
| 292 | == IRQ and FIQ unmasked == |
| 293 | nwd -> nwd : process |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 294 | |
| 295 | **Deliver FIQ to secure world when SCR_NS is cleared** |
| 296 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 297 | .. uml:: |
| 298 | :align: center |
| 299 | :caption: FIQ received while processing an IRQ forwarded from secure world |
| 300 | |
| 301 | participant "Normal World" as nwd |
| 302 | participant "Secure Monitor" as smon |
| 303 | participant "OP-TEE OS entry" as entry |
| 304 | participant "OP-TEE OS" as optee |
| 305 | == IRQ and FIQ unmasked == |
| 306 | optee -> optee : process |
| 307 | == IRQ and FIQ unmasked,\nForeign interrupt received == |
| 308 | optee -> optee : suspend thread |
| 309 | optee -> entry : forward foreign interrupt |
| 310 | entry -> smon : smc: forward foreign interrupt |
| 311 | smon -> smon: Save secure context |
| 312 | smon -> smon: Restore non-secure context |
| 313 | == IRQ and FIQ unmasked == |
| 314 | smon --> nwd : eret: IRQ forwarded |
| 315 | == FIQ unmasked, IRQ received == |
| 316 | nwd -> nwd : process IRQ |
| 317 | == IRQ and FIQ masked,\nSecure interrupt received == |
| 318 | smon -> smon : Save non-secure context |
| 319 | smon -> smon : Restore secure context |
| 320 | smon --> entry : eret: native interrupt entry point |
| 321 | entry -> entry : process received native interrupt |
| 322 | entry -> smon: smc: return |
| 323 | smon -> smon : Save secure context |
| 324 | smon -> smon : Restore non-secure context |
| 325 | smon --> nwd : eret: return to Normal world |
| 326 | == FIQ unmasked\nIRQ still being processed == |
| 327 | nwd -> nwd : process IRQ |
| 328 | == IRQ and FIQ unmasked == |
| 329 | nwd -> smon : smc: return from IRQ |
| 330 | == IRQ and FIQ masked == |
| 331 | smon -> smon : Save non-secure context |
| 332 | smon -> smon : Restore secure context |
| 333 | smon --> entry : eret: return from foreign interrupt |
| 334 | entry -> entry : find thread |
| 335 | entry --> optee : resume execution |
| 336 | == IRQ and FIQ unmasked == |
| 337 | optee -> optee : process |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 338 | |
| 339 | Trusted thread scheduling |
| 340 | ========================= |
| 341 | **Trusted thread for standard services** |
| 342 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 343 | OP-TEE yielding services are carried through standard SMC. Execution of these |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 344 | services can be interrupted by foreign interrupts. To suspend and restore the |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 345 | service execution, optee_os assigns a trusted thread at yielding SMC entry. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 346 | |
| 347 | The trusted thread terminates when optee_os returns to the normal world with a |
| 348 | service completion status. |
| 349 | |
| 350 | A trusted thread execution can be interrupted by a native interrupt. In this |
| 351 | case the native interrupt is handled by the interrupt exception handlers and |
| 352 | once served, optee_os returns to the execution trusted thread. |
| 353 | |
| 354 | A trusted thread execution can be interrupted by a foreign interrupt. In this |
| 355 | case, optee_os suspends the trusted thread and invokes the normal world through |
| 356 | the Monitor (optee_os so-called RPC services). The trusted threads will resume |
| 357 | only once normal world invokes the optee_os with the RPC service status. |
| 358 | |
| 359 | A trusted thread execution can lead optee_os to invoke a service in normal |
| 360 | world: access a file, get the REE current time, etc. The trusted thread is |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 361 | first suspended then resumed during remote service execution. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 362 | |
| 363 | **Scheduling considerations** |
| 364 | |
| 365 | When a trusted thread is interrupted by a foreign interrupt and when optee_os |
| 366 | invokes a normal world service, the normal world gets the opportunity to |
| 367 | reschedule the running applications. The trusted thread will resume only once |
| 368 | the client application is scheduled back. Thus, a trusted thread execution |
| 369 | follows the scheduling of the normal world caller context. |
| 370 | |
| 371 | Optee_os does not implement any thread scheduling. Each trusted thread is |
| 372 | expected to track a service that is invoked from the normal world and should |
| 373 | return to it with an execution status. |
| 374 | |
| 375 | The OP-TEE Linux driver (as implemented in `drivers/tee/optee`_ since Linux |
| 376 | kernel 4.12) is designed so that the Linux thread invoking OP-TEE gets assigned |
| 377 | a trusted thread on TEE side. The execution of the trusted thread is tied to the |
| 378 | execution of the caller Linux thread which is under the Linux kernel scheduling |
| 379 | decision. This means trusted threads are scheduled by the Linux kernel. |
| 380 | |
| 381 | **Trusted thread constraints** |
| 382 | |
| 383 | TEE core handles a static number of trusted threads, see ``CFG_NUM_THREADS``. |
| 384 | |
Jens Wiklander | 01d61a9 | 2021-05-12 14:56:22 +0200 | [diff] [blame] | 385 | Trusted threads are expensive on memory constrained system, mainly |
| 386 | because of the execution stack size. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 387 | |
| 388 | On SMP systems, optee_os can execute several trusted threads in parallel if the |
| 389 | normal world supports scheduling of processes. Even on UP systems, supporting |
| 390 | several trusted threads in optee_os helps normal world scheduler to be |
| 391 | efficient. |
| 392 | |
Etienne Carriere | eda1e0f | 2023-05-07 17:29:28 +0200 | [diff] [blame] | 393 | Core handlers for native interrupts |
| 394 | =================================== |
| 395 | |
| 396 | OP-TEE core provides methods for device drivers to setup and register |
| 397 | handler functions for native interrupt controller drivers |
| 398 | (see:ref:`native_foreign_irqs`). |
| 399 | Interrupt handlers can be nested as when an interrupt controller |
| 400 | exposes interrupts which signaling is multiplexed on an interrupt |
| 401 | controlled by a parent interrupt controller. |
| 402 | |
| 403 | Interrupt controllers are represented by an instance of ``struct itr_chip``. |
| 404 | An interrupt controller exposes a given number of interrupts, each identified |
| 405 | by an index from 0 to N-1 where N is the total number of interrupts exposed |
| 406 | by that controller. In the literature, an interrupt index identifier |
| 407 | is called interrupt number. |
| 408 | |
| 409 | **Interrupt management API functions** |
| 410 | |
| 411 | Interrupt management resources are declared in header file interrupt.h_. |
| 412 | Interrupt consumers main API functions are: |
| 413 | |
| 414 | Â Â - ``interrupt_enable()`` and ``interrupt_disable()`` to respectively |
| 415 | Â Â Â enable or disable an interrupt. |
| 416 | |
| 417 | Â Â - ``interrupt_mask()`` and ``interrupt_unmask()`` to respectively mask |
| 418 | Â Â Â or unmask an interrupt. Masking of an enabled interrupt temporarily |
| 419 | Â Â Â disables the interrupt while unmasking enables a previously masked |
| 420 | Â Â Â interrupt. ``interrupt_mask()`` and ``interrupt_unmask()`` are |
| 421 | Â Â Â allowed to be called from an interrupt context, but |
| 422 | Â Â Â ``interrupt_enable()`` and ``interrupt_disable()`` not so. |
| 423 | |
| 424 | Â Â - ``interrupt_configure()`` to configure an interrupt detection mode |
| 425 | Â Â Â and priority. |
| 426 | |
| 427 | Â Â - ``interrupt_add_handler()`` to configure an interrupt and register |
| 428 | an interrupt handler function, see below. |
| 429 | |
| 430 | - ``interrupt_remove_handler()`` to unregister an interrupt handler |
| 431 | function from an interrupt. |
| 432 | |
| 433 | **Interrupt controller drivers** |
| 434 | |
| 435 | An interrupt controller instance, named chip (``struct itr_chip``) defines |
| 436 | operation function handlers for management of the interrupt(s) it controls. |
| 437 | An interrupt chip driver must provide operation handler functions ``.add``, |
| 438 | ``.mask``, ``.unmask``, ``.enable`` and ``.disable``. There are other |
| 439 | operation handler functions that are optional, as for example ``.rasie_pi``, |
| 440 | ``.raise_sgi`` and ``.set_priority``. |
| 441 | |
| 442 | An interrupt chip driver registers the controller instance with API |
| 443 | function ``itr_chip_init()``. The driver calls the registered interrupt |
| 444 | consumer(s) handler(s) with API function ``interrupt_call_handlers()``. |
| 445 | |
| 446 | **CPU main interrupt controller driver** |
| 447 | |
| 448 | The CPU interrupt controller (e.g. a GIC instance on Arm architecture CPUs) |
| 449 | is called the main interrupt controller. Its driver must register as main |
| 450 | controller using API function ``interrupt_main_init()``. The function is |
| 451 | in charge of calling ``itr_chip_init()`` for that chip instance. |
| 452 | |
| 453 | Interrupt consumer drivers can get a reference to the main interrupt |
| 454 | controller with the API function ``interrupt_get_main_chip()``. |
| 455 | |
| 456 | **Interrupt handlers** |
| 457 | |
| 458 | Interrupt handler functions are callback functions registered by interrupt |
| 459 | consumer drivers that core shall call when the related interrupt occurs. |
| 460 | Structure ``struct itr_handler`` references a handler. It contains the |
| 461 | handler function entry point, the interrupt number, the interrupt controller |
| 462 | device and a few more parameters. |
| 463 | |
| 464 | An interrupt handler function return value is of type ``enum itr_return``. |
| 465 | It shall return ``ITRR_HANDLED`` when the interrupt is served and |
| 466 | ``ITRR_NONE`` when the interrupt cannot be served. |
| 467 | |
| 468 | The interrupt handler runs in an interrupt context rather than a thread |
| 469 | context. When this occurs, all other interrupts are masked, necessitating fast |
| 470 | execution of the interrupt handler to avoid delaying or missing out on other |
| 471 | interrupts. When an interrupt occurs that requires the completion of |
| 472 | long-running operations, the interrupt handler should request the OP-TEE |
| 473 | bottom half thread (see :ref:`notifications`) to execute those operations. |
| 474 | |
| 475 | API function ``interrupt_add_handler()``, |
| 476 | ``interrupt_add_handler_with_chip()`` and ``interrupt_alloc_add_handler()`` |
| 477 | configure and register a handler function to a given interrupt. |
| 478 | |
| 479 | API function ``interrupt_remove_handler()`` and |
| 480 | ``interrupt_remove_free_handler()`` unregister a registered handler. |
| 481 | |
| 482 | **Interrupt consumer driver** |
| 483 | |
| 484 | A typical implementation of a driver consuming an interrupt includes |
| 485 | retrieving of the interrupt resource (interrupt controller and interrupt |
| 486 | number in that controller), configuring the interrupt, registering a handler |
| 487 | for the interrupt and enabling/disabling the interrupt. |
| 488 | |
| 489 | For example, the dummy driver below prints a debug trace when the related |
| 490 | interrupt occurs: |
| 491 | |
| 492 | .. code-block:: c |
| 493 | |
| 494 | static struct itr_handler *foo_int1_handler; |
| 495 | |
| 496 | static struct foo_int1_data = { |
| 497 | /* field with some interrupt handler private data */ |
| 498 | }; |
| 499 | |
| 500 | static enum itr_return foo_it_handler_fn(struct itr_handler *h) |
| 501 | { |
| 502 | foo_acknowledge_interrupt(h->it);. |
| 503 | DMSG("Interrupt FOO%u served", h->it); |
| 504 | |
| 505 | return ITRR_HANDLED; |
| 506 | } |
| 507 | |
| 508 | static TEE_Result foo_initialization(void) |
| 509 | { |
| 510 | TEE_Result res = TEE_ERROR_GENERIC; |
| 511 | |
| 512 | res = interrupt_alloc_add_handler(itr_core_get(), |
| 513 | GIC_INT_FOO, |
| 514 | foo_it_handler_fn, |
| 515 | ITRF_TRIGGER_LEVEL, |
| 516 | &foo_int1_data, |
| 517 | &foo_int1_handler); |
| 518 | if (res) |
| 519 | return res; |
| 520 | |
| 521 | interrupt_enable(itr_chip, it_num); |
| 522 | |
| 523 | return TEE_SUCCESS; |
| 524 | } |
| 525 | |
| 526 | static void foo_release(void) |
| 527 | { |
| 528 | if (foo_int1_handler) { |
| 529 | interrupt_disable(foo_int1_handler->chip, |
| 530 | foo_int1_handler->it); |
| 531 | |
| 532 | interrupt_remove_free_handler(&foo_int1_handler); |
| 533 | } |
| 534 | } |
| 535 | |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 536 | ---- |
| 537 | |
Jens Wiklander | 5507c02 | 2021-05-14 15:12:56 +0200 | [diff] [blame] | 538 | .. _notifications: |
| 539 | |
| 540 | Notifications |
| 541 | ************* |
| 542 | |
| 543 | There are two kinds of notifications that secure world can use to make |
| 544 | normal world aware of some event. |
| 545 | |
| 546 | 1. Synchronous notifications delivered with ``OPTEE_RPC_CMD_NOTIFICATION`` |
| 547 | using the ``OPTEE_RPC_NOTIFICATION_SEND`` parameter. |
| 548 | 2. Asynchronous notifications delivered with a combination of a non-secure |
| 549 | interrupt and a fast call from the non-secure interrupt handler. |
| 550 | |
| 551 | Secure world can wait in normal for a notification to arrive. This allows |
| 552 | the calling thread to sleep instead of spinning when waiting for something. |
| 553 | This happens for instance when a thread waits for a mutex to become |
| 554 | available. |
| 555 | |
| 556 | Synchronous notifications are limited by depending on RPC for delivery, this |
| 557 | is only usable from a normal thread context. Secure interrupt handler or |
| 558 | other atomic context cannot use synchronous notifications due to this. |
| 559 | |
| 560 | Asynchrononous notifications uses a platform specific way of triggering a |
| 561 | non-secure interrupt. This is done with ``itr_raise_pi()`` in a way |
| 562 | suitable for a secure interrupt handler or another atomic context. This is |
| 563 | useful when using a top half and bottom half kind of design in a device |
| 564 | driver. The top half is done in the secure interrupt handler which then |
| 565 | triggers normal world to make a yielding call into secure world to do the |
| 566 | bottom half processing. |
| 567 | |
| 568 | .. uml:: |
| 569 | :align: center |
| 570 | :caption: Top half, bottom half example |
| 571 | |
| 572 | participant "OP-TEE OS\ninterrupt handler" as sec_itr |
| 573 | participant "OP-TEE OS\nfastcall handler" as fastcall |
| 574 | participant "Interrupt\ncontroller" as itc |
| 575 | participant "Normal World\ninterrupt handler" as ns_itr |
| 576 | participant "Normal World\nthread" as ns_thr |
| 577 | participant "OP-TEE OS\nyielding do bottom half" as bottom |
| 578 | |
| 579 | itc --> sec_itr : Secure interrupt |
| 580 | activate sec_itr |
| 581 | sec_itr -> sec_itr : Top half processing |
| 582 | sec_itr --> itc : Trigger NS interrupt |
| 583 | itc --> ns_itr : Non-secure interrupt |
| 584 | activate ns_itr |
| 585 | sec_itr --> itc: End of interrupt |
| 586 | deactivate sec_itr |
| 587 | ns_itr -> fastcall ++: Get notification |
| 588 | fastcall -> ns_itr --: Return notification |
| 589 | alt Do bottom half notifcation |
| 590 | ns_itr --> ns_thr : Wake thread |
| 591 | activate ns_thr |
| 592 | ns_itr --> itc: End of interrupt |
| 593 | deactivate ns_itr |
| 594 | ns_thr -> bottom ++: Do bottom half |
| 595 | bottom -> bottom : Process bottom half |
| 596 | bottom -> ns_thr --: Done |
| 597 | deactivate ns_thr |
| 598 | else Some other notification |
| 599 | end |
| 600 | |
| 601 | .. uml:: |
| 602 | :align: center |
| 603 | :caption: Synchronous example |
| 604 | |
| 605 | participant "OP-TEE OS\nthread 1" as sec_thr1 |
| 606 | participant "Normal World\nthread 1" as ns_thr1 |
| 607 | participant "OP-TEE OS\nthread 2" as sec_thr2 |
| 608 | participant "Normal World\nthread 2" as ns_thr2 |
| 609 | |
| 610 | activate ns_thr1 |
| 611 | ns_thr1 -> sec_thr1 ++ : Invoke |
| 612 | sec_thr1 -> sec_thr1 : Lock mutex |
| 613 | sec_thr1 -> sec_thr1 : Process |
| 614 | activate ns_thr2 |
| 615 | ns_thr2 -> sec_thr2 ++: Invoke |
| 616 | sec_thr2 -> ns_thr2 -- : RPC: Wait for mutex |
| 617 | ns_thr2 -> ns_thr2 : Wait for notifcation |
| 618 | deactivate ns_thr2 |
| 619 | sec_thr1 -> sec_thr1 : Unlock mutex |
| 620 | sec_thr1 -> ns_thr1 -- : RPC: Notify mutex unlocked |
| 621 | ns_thr1 --> ns_thr2 : Notify mutex unlocked |
| 622 | activate ns_thr2 |
| 623 | ns_thr1 -> sec_thr1 ++ : Return from RPC |
| 624 | sec_thr1 -> sec_thr1 : Process |
| 625 | sec_thr1 -> ns_thr1 -- : Return from Invoke |
| 626 | deactivate ns_thr1 |
| 627 | ns_thr2 -> sec_thr2 ++ : Return from RPC |
| 628 | sec_thr2 -> sec_thr2 : Lock mutex |
| 629 | sec_thr2 -> sec_thr2 : Process |
| 630 | sec_thr2 -> sec_thr2 : Unlock mutex |
| 631 | sec_thr2 -> sec_thr2 : Process |
| 632 | sec_thr2 -> ns_thr2 -- : Return from Invoke |
| 633 | deactivate ns_thr2 |
| 634 | |
| 635 | Notifications are identified with a value, allocated as: |
| 636 | |
| 637 | 0 - 63 |
| 638 | Mixed asynchronous and synchronous range |
| 639 | 64 - Max |
| 640 | Synchronous only range |
| 641 | |
| 642 | If the **Max** value is smaller than 63, then there's only the mixed range. |
| 643 | |
| 644 | If asynchronous notifications are enabled then is the value 0 reserved for |
Joakim Bech | 3cb68c6 | 2023-03-08 14:55:26 +0100 | [diff] [blame] | 645 | signalling the a driver need a bottom half call, that is the yielding call |
Jens Wiklander | 5507c02 | 2021-05-14 15:12:56 +0200 | [diff] [blame] | 646 | ``OPTEE_MSG_CMD_DO_BOTTOM_HALF``. |
| 647 | |
| 648 | The rest of the asynchronous notification values are managed with two |
| 649 | functions ``notif_alloc_async_value()`` and ``notif_free_async_value()``. |
| 650 | |
| 651 | ---- |
| 652 | |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 653 | .. _memory_objects: |
| 654 | |
| 655 | Memory objects |
| 656 | ************** |
| 657 | A memory object, **MOBJ**, describes a piece of memory. The interface provided |
| 658 | is mostly abstract when it comes to using the MOBJ to populate translation |
| 659 | tables etc. There are different kinds of MOBJs describing: |
| 660 | |
| 661 | - Physically contiguous memory |
| 662 | - created with ``mobj_phys_alloc(...)``. |
| 663 | |
| 664 | - Virtual memory |
| 665 | - one instance with the name ``mobj_virt`` available. |
| 666 | - spans the entire virtual address space. |
| 667 | |
| 668 | - Physically contiguous memory allocated from a ``tee_mm_pool_t *`` |
| 669 | - created with ``mobj_mm_alloc(...)``. |
| 670 | |
| 671 | - Paged memory |
| 672 | - created with ``mobj_paged_alloc(...)``. |
| 673 | - only contains the supplied size and makes ``mobj_is_paged(...)`` |
| 674 | return true if supplied as argument. |
| 675 | |
| 676 | - Secure copy paged shared memory |
| 677 | - created with ``mobj_seccpy_shm_alloc(...)``. |
| 678 | - makes ``mobj_is_paged(...)`` and ``mobj_is_secure(...)`` return true |
| 679 | if supplied as argument. |
| 680 | |
| 681 | ---- |
| 682 | |
| 683 | .. _mmu: |
| 684 | |
| 685 | MMU |
| 686 | *** |
| 687 | Translation tables |
| 688 | ================== |
Jens Wiklander | 18b953f | 2021-05-14 13:00:21 +0200 | [diff] [blame] | 689 | |
| 690 | OP-TEE supports two translation table formats: |
| 691 | |
| 692 | 1. Short-descriptor translation table format, available on ARMv7-A and |
| 693 | ARMv8-A AArch32 |
| 694 | 2. Long-descriptor translation format, available on ARMv7-A with LPAE and |
| 695 | ARMv8-A |
| 696 | |
| 697 | ARMv7-A without LPAE (Large Physical Address Extension) must use the |
| 698 | short-descriptor translation table format only. ARMv8-A AArch64 must use |
| 699 | the long-descriptor translation format only. |
| 700 | |
| 701 | Translation table format is a static build time configuration option, |
| 702 | ``CFG_WITH_LPAE``. The design around the translation table handling has |
| 703 | been centered around these factors: |
| 704 | |
| 705 | 1. Share translation tables between CPUs when possible to save memory |
| 706 | and simplify paging |
| 707 | 2. Support non-global CPU specific mappings to allow executing different |
| 708 | TAs in parallel. |
| 709 | |
| 710 | Short-descriptor translation table format |
| 711 | ----------------------------------------- |
| 712 | |
| 713 | Several L1 translation tables are used, one large spanning 4 GiB and two or |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 714 | more small tables spanning 32 MiB. The large translation table handles kernel |
| 715 | mode mapping and matches all addresses not covered by the small translation |
| 716 | tables. The small translation tables are assigned per thread and covers the |
| 717 | mapping of the virtual memory space for one TA context. |
| 718 | |
Ding Tao | 7620b4c | 2022-05-10 13:42:19 +0000 | [diff] [blame] | 719 | Memory space between small and large translation table is configured by TTBCR. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 720 | TTBR1 always points to the large translation table. TTBR0 points to the a small |
| 721 | translation table when user mapping is active and to the large translation table |
| 722 | when no user mapping is currently active. For details about registers etc, |
| 723 | please refer to a Technical Reference Manual for your architecture, for example |
| 724 | `Cortex-A53 TRM`_. |
| 725 | |
| 726 | The translation tables has certain alignment constraints, the alignment (of the |
| 727 | physical address) has to be the same as the size of the translation table. The |
| 728 | translation tables are statically allocated to avoid fragmentation of memory due |
| 729 | to the alignment constraints. |
| 730 | |
| 731 | Each thread has one small L1 translation table of its own. Each TA context has a |
| 732 | compact representation of its L1 translation table. The compact representation |
| 733 | is used to initialize the thread specific L1 translation table when the TA |
| 734 | context is activated. |
| 735 | |
| 736 | .. graphviz:: |
Jens Wiklander | 18b953f | 2021-05-14 13:00:21 +0200 | [diff] [blame] | 737 | :align: center |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 738 | |
| 739 | digraph xlat_table { |
| 740 | graph [ |
| 741 | rankdir = "LR" |
| 742 | ]; |
| 743 | node [ |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 744 | shape = "ellipse" |
| 745 | ]; |
| 746 | edge [ |
| 747 | ]; |
| 748 | "node_ttb" [ |
| 749 | label = "<f0> TTBR0 | <f1> TTBR1" |
| 750 | shape = "record" |
| 751 | ]; |
| 752 | "node_large_l1" [ |
| 753 | label = "<f0> Large L1\nSpans 4 GiB" |
| 754 | shape = "record" |
| 755 | ]; |
| 756 | "node_small_l1" [ |
| 757 | label = "Small L1\nSpans 32 MiB\nper entry | <f0> 0 | <f1> 1 | ... | <fn> n" |
| 758 | shape = "record" |
| 759 | ]; |
| 760 | |
| 761 | "node_ttb":f0 -> "node_small_l1":f0 [ label = "Thread 0 ctx active" ]; |
| 762 | "node_ttb":f0 -> "node_small_l1":f1 [ label = "Thread 1 ctx active" ]; |
| 763 | "node_ttb":f0 -> "node_small_l1":fn [ label = "Thread n ctx active" ]; |
| 764 | "node_ttb":f0 -> "node_large_l1" [ label="No active ctx" ]; |
| 765 | "node_ttb":f1 -> "node_large_l1"; |
| 766 | } |
| 767 | |
Jens Wiklander | 18b953f | 2021-05-14 13:00:21 +0200 | [diff] [blame] | 768 | Long-descriptor translation table format |
| 769 | ---------------------------------------- |
| 770 | |
| 771 | Each CPU is assigned a L1 translation table which is programmed into |
| 772 | Translation Table Base Register 0 (``TTBR0`` or ``TTBR0_EL1`` as |
| 773 | appropriate). |
| 774 | |
| 775 | L1 and L2 translation tables are statically allocated and initialized at |
| 776 | boot. Normally there is only one shared L2 table, but with ASLR enabled the |
| 777 | virtual address space used for the shared mapping may need to use two |
| 778 | tables. An unused entry in the L1 table is selected to point to the per |
| 779 | thread L2 table. With ASLR configured this means that different per thread |
| 780 | entry may be selected each time the system boots. Note that this entry will |
| 781 | only point to a table when the per thread mapping is activated. |
| 782 | |
| 783 | The L2 translation tables in their turn point to L3 tables which use the |
| 784 | small page granularity of 4 KiB. The shared mappings has the L3 tables |
| 785 | initialized too at boot, but the per thread L3 tables are dynamic and are |
| 786 | only assigned when the mapping is activated. |
| 787 | |
| 788 | .. graphviz:: |
| 789 | :align: center |
| 790 | :caption: Example translation table setup with 4GiB virtual address space |
| 791 | with L3 tables excluded |
| 792 | |
| 793 | digraph xlat_table { |
| 794 | graph [ rankdir = "LR" ]; |
| 795 | node [ ]; |
| 796 | edge [ ]; |
| 797 | |
| 798 | "ttbr0" [ |
| 799 | label = "TTBR0" |
| 800 | shape = "record" |
| 801 | ]; |
| 802 | "node_l1" [ |
| 803 | label = "<h> Per CPU L1 table | <f0> 0 | <f1> 1 | <f2> 2 | <f3> 3" |
| 804 | shape = "record" |
| 805 | ]; |
| 806 | "shared_l2_n" [ |
| 807 | label = "<h> Shared L2 table n | 0 | ... | 512" |
| 808 | shape = "record" |
| 809 | ] |
| 810 | "shared_l2_m" [ |
| 811 | label = "<h> Shared L2 table m | 0 | ... | 512" |
| 812 | shape = "record" |
| 813 | ] |
| 814 | "per_thread_l2" [ |
| 815 | label = "<h> Per thread L2 table | 0 | ... | 512" |
| 816 | shape = "record" |
| 817 | ] |
| 818 | "ttbr0" -> "node_l1":h; |
| 819 | "node_l1":f2 -> "shared_l2_n":h; |
| 820 | "node_l1":f3 -> "shared_l2_m":h; |
| 821 | "node_l1":f0 -> "per_thread_l2":h; |
| 822 | } |
| 823 | |
| 824 | |
Jens Wiklander | 03b05a0 | 2019-02-25 13:44:38 +0100 | [diff] [blame] | 825 | Page table cache |
| 826 | ================ |
| 827 | Page tables used to map TAs are managed with the page table cache. When the |
| 828 | context of a TA is unmapped, all its page tables are released with a call |
| 829 | to ``pgt_free()``. All page tables needed when mapping a TA are allocated |
| 830 | using ``pgt_alloc()``. |
| 831 | |
| 832 | A fixed maximum number of translation tables are available in a pool. One |
| 833 | thread may execute a TA which needs all or almost all tables. This can |
| 834 | block TAs from being executed by other threads. To ensure that all TAs |
| 835 | eventually will be permitted to execute ``pgt_alloc()`` temporarily frees |
| 836 | eventual tables allocated before waiting for tables to become available. |
| 837 | |
| 838 | The page table cache behaves differently depending on configuration |
| 839 | options. |
| 840 | |
| 841 | Without paging (``CFG_WITH_PAGER=n``) |
| 842 | ------------------------------------- |
| 843 | This is the easiest configuration. All page tables are statically allocated |
| 844 | in the ``.nozi.pgt_cache`` section. ``pgt_alloc()`` allocates tables from the |
| 845 | free-list and ``pgt_free()`` returns the tables directly to the free-list. |
| 846 | |
| 847 | With paging enabled (``CFG_WITH_PAGER=y``) |
| 848 | ------------------------------------------ |
| 849 | |
| 850 | Page tables are allocated as zero initialized locked pages during boot |
| 851 | using ``tee_pager_alloc()``. Locked pages are populated with physical pages |
| 852 | on demand from the pager. The physical page can be released when not needed |
| 853 | any longer with ``tee_pager_release_phys()``. |
| 854 | |
| 855 | With ``CFG_WITH_LPAE=y`` each translation table has the same size as a |
| 856 | physical page which makes it easy to release the physical page when the |
| 857 | translation table isn't needed any longer. With the short-descriptor table |
| 858 | format (``CFG_WITH_LPAE=n``) it becomes more complicated as four |
| 859 | translation tables are stored in each page. Additional bookkeeping is used |
| 860 | to tell when the page for used by four separate translation tables can be |
| 861 | released. |
| 862 | |
| 863 | With paging of user TA enabled (``CFG_PAGED_USER_TA=y``) |
| 864 | -------------------------------------------------------- |
| 865 | With paging of user TAs enabled a cache of recently used translation tables |
| 866 | is used. This can save us from a storm of page faults when restoring the |
| 867 | mappings of a recently unmapped TA. Which translation tables should be |
| 868 | cached is indicated with reference counting by the pager on used tables. |
| 869 | When a table needs to be forcefully freed |
| 870 | ``tee_pager_pgt_save_and_release_entries()`` is called to let the pager |
| 871 | know that the table can't be used any longer. |
| 872 | |
| 873 | When a mapping in a TA is removed it also needs to be purged from cached |
| 874 | tables with ``pgt_flush_ctx_range()`` to prevent old mappings from being |
| 875 | accidentally reused. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 876 | |
| 877 | Switching to user mode |
| 878 | ====================== |
| 879 | This section only applies with following configuration flags: |
| 880 | |
| 881 | - ``CFG_WITH_LPAE=n`` |
| 882 | - ``CFG_CORE_UNMAP_CORE_AT_EL0=y`` |
| 883 | |
| 884 | When switching to user mode only a minimal kernel mode mapping is kept. This is |
| 885 | achieved by selecting a zeroed out big L1 translation in TTBR1 when |
| 886 | transitioning to user mode. When returning back to kernel mode the original L1 |
| 887 | translation table is restored in TTBR1. |
| 888 | |
| 889 | Switching to normal world |
| 890 | ========================= |
| 891 | When switching to normal world either via a foreign interrupt (see |
| 892 | :ref:`native_foreign_irqs` or RPC there is a chance that secure world will |
| 893 | resume execution on a different CPU. This means that the new CPU need to be |
| 894 | configured with the context of the currently active TA. This is solved by always |
Jens Wiklander | ddde3a8 | 2019-02-25 12:46:18 +0100 | [diff] [blame] | 895 | setting the TA context in the CPU when resuming execution. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 896 | |
| 897 | ---- |
| 898 | |
| 899 | .. _pager: |
| 900 | |
| 901 | Pager |
| 902 | ***** |
| 903 | OP-TEE currently requires >256 KiB RAM for OP-TEE kernel memory. This is not a |
| 904 | problem if OP-TEE uses TrustZone protected DDR, but for security reasons OP-TEE |
| 905 | may need to use TrustZone protected SRAM instead. The amount of available SRAM |
| 906 | varies between platforms, from just a few KiB up to over 512 KiB. Platforms with |
| 907 | just a few KiB of SRAM cannot be expected to be able to run a complete TEE |
| 908 | solution in SRAM. But those with 128 to 256 KiB of SRAM can be expected to have |
| 909 | a capable TEE solution in SRAM. The pager provides a solution to this by demand |
| 910 | paging parts of OP-TEE using virtual memory. |
| 911 | |
| 912 | Secure memory |
| 913 | ============= |
| 914 | TrustZone protected SRAM is generally considered more secure than TrustZone |
| 915 | protected DRAM as there is usually more attack vectors on DRAM. The attack |
| 916 | vectors are hardware dependent and can be different for different platforms. |
| 917 | |
| 918 | Backing store |
| 919 | ============= |
| 920 | TrustZone protected DRAM or in some cases non-secure DRAM is used as backing |
| 921 | store. The data in the backing store is integrity protected with one hash |
| 922 | (SHA-256) per page (4KiB). Readonly pages are not encrypted since the OP-TEE |
| 923 | binary itself is not encrypted. |
| 924 | |
| 925 | Partitioning of memory |
| 926 | ====================== |
| 927 | The code that handles demand paging must always be available as it would |
| 928 | otherwise lead to deadlock. The virtual memory is partitioned as: |
| 929 | |
| 930 | +--------------+-------------------+ |
| 931 | | Type | Sections | |
| 932 | +==============+===================+ |
| 933 | | unpaged | | text | |
| 934 | | | | rodata | |
| 935 | | | | data | |
| 936 | | | | bss | |
| 937 | | | | heap1 | |
| 938 | | | | nozi | |
| 939 | | | | heap2 | |
| 940 | +--------------+-------------------+ |
| 941 | | init / paged | | text_init | |
| 942 | | | | rodata_init | |
| 943 | +--------------+-------------------+ |
| 944 | | paged | | text_pageable | |
| 945 | | | | rodata_pageable | |
| 946 | +--------------+-------------------+ |
| 947 | | demand alloc | | |
| 948 | +--------------+-------------------+ |
| 949 | |
| 950 | Where ``nozi`` stands for "not zero initialized", this section contains entry |
| 951 | stacks (thread stack when TEE pager is not enabled) and translation tables (TEE |
| 952 | pager cached translation table when the pager is enabled and LPAE MMU is used). |
| 953 | |
| 954 | The ``init`` area is available when OP-TEE is initializing and contains |
| 955 | everything that is needed to initialize the pager. After the pager has been |
| 956 | initialized this area will be used for demand paged instead. |
| 957 | |
| 958 | The ``demand alloc`` area is a special area where the pages are allocated and |
| 959 | removed from the pager on demand. Those pages are returned when OP-TEE does not |
| 960 | need them any longer. The thread stacks currently belongs this area. This means |
| 961 | that when a stack is not used the physical pages can be used by the pager for |
| 962 | better performance. |
| 963 | |
| 964 | The technique to gather code in the different area is based on compiling all |
| 965 | functions and data into separate sections. The unpaged text and rodata is then |
| 966 | gathered by linking all object files with ``--gc-sections`` to eliminate |
| 967 | sections that are outside the dependency graph of the entry functions for |
| 968 | unpaged functions. A script analyzes this ELF file and generates the bits of the |
| 969 | final link script. The process is repeated for init text and rodata. What is |
| 970 | not "unpaged" or "init" becomes "paged". |
| 971 | |
| 972 | Partitioning of the binary |
| 973 | ========================== |
| 974 | .. note:: |
| 975 | The struct definitions provided in this section are explicitly covered by |
| 976 | the following dual license: |
| 977 | |
| 978 | .. code-block:: none |
| 979 | |
| 980 | SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0) |
| 981 | |
| 982 | The binary is partitioned into four parts as: |
| 983 | |
| 984 | |
| 985 | +----------+ |
| 986 | | Binary | |
| 987 | +==========+ |
| 988 | | Header | |
| 989 | +----------+ |
| 990 | | Init | |
| 991 | +----------+ |
| 992 | | Hashes | |
| 993 | +----------+ |
| 994 | | Pageable | |
| 995 | +----------+ |
| 996 | |
| 997 | The header is defined as: |
| 998 | |
| 999 | .. code-block:: c |
| 1000 | |
| 1001 | #define OPTEE_MAGIC 0x4554504f |
| 1002 | #define OPTEE_VERSION 1 |
| 1003 | #define OPTEE_ARCH_ARM32 0 |
| 1004 | #define OPTEE_ARCH_ARM64 1 |
| 1005 | |
| 1006 | struct optee_header { |
| 1007 | uint32_t magic; |
| 1008 | uint8_t version; |
| 1009 | uint8_t arch; |
| 1010 | uint16_t flags; |
| 1011 | uint32_t init_size; |
| 1012 | uint32_t init_load_addr_hi; |
| 1013 | uint32_t init_load_addr_lo; |
| 1014 | uint32_t init_mem_usage; |
| 1015 | uint32_t paged_size; |
| 1016 | }; |
| 1017 | |
| 1018 | The header is only used by the loader of OP-TEE, not OP-TEE itself. To |
| 1019 | initialize OP-TEE the loader loads the complete binary into memory and copies |
| 1020 | what follows the header and the following ``init_size`` bytes to |
| 1021 | ``(init_load_addr_hi << 32 | init_load_addr_lo)``. ``init_mem_usage`` is used by |
| 1022 | the loader to be able to check that there is enough physical memory available |
| 1023 | for OP-TEE to be able to initialize at all. The loader supplies in ``r0/x0`` the |
| 1024 | address of the first byte following what was not copied and jumps to the load |
| 1025 | address to start OP-TEE. |
| 1026 | |
| 1027 | In addition to overall binary with partitions inside described as above, three |
| 1028 | extra binaries are generated simultaneously during build process for loaders who |
| 1029 | support loading separate binaries: |
| 1030 | |
| 1031 | +-----------+ |
| 1032 | | v2 binary | |
| 1033 | +===========+ |
| 1034 | | Header | |
| 1035 | +-----------+ |
| 1036 | |
| 1037 | +-----------+ |
| 1038 | | v2 binary | |
| 1039 | +===========+ |
| 1040 | | Init | |
| 1041 | +-----------+ |
| 1042 | | Hashes | |
| 1043 | +-----------+ |
| 1044 | |
| 1045 | +-----------+ |
| 1046 | | v2 binary | |
| 1047 | +===========+ |
| 1048 | | Pageable | |
| 1049 | +-----------+ |
| 1050 | |
| 1051 | In this case, loaders load header binary first to get image list and information |
| 1052 | of each image; and then load each of them into specific load address assigned in |
| 1053 | structure. These binaries are named with `v2` suffix to distinguish from the |
| 1054 | existing binaries. Header format is updated to help loaders loading binaries |
| 1055 | efficiently: |
| 1056 | |
| 1057 | .. code-block:: c |
| 1058 | |
| 1059 | #define OPTEE_IMAGE_ID_PAGER 0 |
| 1060 | #define OPTEE_IMAGE_ID_PAGED 1 |
| 1061 | |
| 1062 | struct optee_image { |
| 1063 | uint32_t load_addr_hi; |
| 1064 | uint32_t load_addr_lo; |
| 1065 | uint32_t image_id; |
| 1066 | uint32_t size; |
| 1067 | }; |
| 1068 | |
| 1069 | struct optee_header_v2 { |
| 1070 | uint32_t magic; |
| 1071 | uint8_t version; |
| 1072 | uint8_t arch; |
| 1073 | uint16_t flags; |
| 1074 | uint32_t nb_images; |
| 1075 | struct optee_image optee_image[]; |
| 1076 | }; |
| 1077 | |
| 1078 | Magic number and architecture are identical as original. Version is increased to |
| 1079 | two. ``load_addr_hi`` and ``load_addr_lo`` may be ``0xFFFFFFFF`` for pageable |
| 1080 | binary since pageable part may get loaded by loader into dynamic available |
| 1081 | position. ``image_id`` indicates how loader handles current binary. Loaders who |
| 1082 | don't support separate loading just ignore all v2 binaries. |
| 1083 | |
| 1084 | Initializing the pager |
| 1085 | ====================== |
| 1086 | The pager is initialized as early as possible during boot in order to minimize |
| 1087 | the "init" area. The global variable ``tee_mm_vcore`` describes the virtual |
| 1088 | memory range that is covered by the level 2 translation table supplied to |
| 1089 | ``tee_pager_init(...)``. |
| 1090 | |
| 1091 | Assign pageable areas |
| 1092 | --------------------- |
| 1093 | A virtual memory range to be handled by the pager is registered with a call to |
| 1094 | ``tee_pager_add_core_area()``. |
| 1095 | |
| 1096 | .. code-block:: c |
| 1097 | |
| 1098 | bool tee_pager_add_area(tee_mm_entry_t *mm, |
| 1099 | uint32_t flags, |
| 1100 | const void *store, |
| 1101 | const void *hashes); |
| 1102 | |
| 1103 | which takes a pointer to ``tee_mm_entry_t`` to tell the range, flags to tell how |
| 1104 | memory should be mapped (readonly, execute etc), and pointers to backing store |
| 1105 | and hashes of the pages. |
| 1106 | |
| 1107 | Assign physical pages |
| 1108 | --------------------- |
| 1109 | Physical SRAM pages are supplied by calling ``tee_pager_add_pages(...)`` |
| 1110 | |
| 1111 | .. code-block:: c |
| 1112 | |
| 1113 | void tee_pager_add_pages(tee_vaddr_t vaddr, |
| 1114 | size_t npages, |
| 1115 | bool unmap); |
| 1116 | |
| 1117 | ``tee_pager_add_pages(...)`` takes the physical address stored in the entry |
| 1118 | mapping the virtual address ``vaddr`` and ``npages`` entries after that and uses |
| 1119 | it to map new pages when needed. The unmap parameter tells whether the pages |
| 1120 | should be unmapped immediately since they does not contain initialized data or |
| 1121 | be kept mapped until they need to be recycled. The pages in the "init" area are |
| 1122 | supplied with ``unmap == false`` since those page have valid content and are in |
| 1123 | use. |
| 1124 | |
| 1125 | Invocation |
| 1126 | ========== |
| 1127 | The pager is invoked as part of the abort handler. A pool of physical pages are |
| 1128 | used to map different virtual addresses. When a new virtual address needs to be |
| 1129 | mapped a free physical page is mapped at the new address, if a free physical |
| 1130 | page cannot be found the oldest physical page is selected instead. When the page |
| 1131 | is mapped new data is copied from backing store and the hash of the page is |
| 1132 | verified. If it is OK the pager returns from the exception to resume the |
| 1133 | execution. |
| 1134 | |
Jens Wiklander | aecf441 | 2019-02-26 12:33:14 +0100 | [diff] [blame] | 1135 | Data structures |
| 1136 | =============== |
| 1137 | .. figure:: ../images/core/tee_pager_area.png |
| 1138 | :figclass: align-center |
| 1139 | |
| 1140 | How the main pager data structures relates to each other |
| 1141 | |
| 1142 | ``struct tee_pager_area`` |
| 1143 | ------------------------- |
| 1144 | This is a central data structure when handling paged |
| 1145 | memory ranges. It's defined as: |
| 1146 | |
| 1147 | .. code-block:: c |
| 1148 | |
| 1149 | struct tee_pager_area { |
| 1150 | struct fobj *fobj; |
| 1151 | size_t fobj_pgoffs; |
| 1152 | enum tee_pager_area_type type; |
| 1153 | uint32_t flags; |
| 1154 | vaddr_t base; |
| 1155 | size_t size; |
| 1156 | struct pgt *pgt; |
| 1157 | TAILQ_ENTRY(tee_pager_area) link; |
| 1158 | TAILQ_ENTRY(tee_pager_area) fobj_link; |
| 1159 | }; |
| 1160 | |
| 1161 | Where ``base`` and ``size`` tells the memory range and ``fobj`` and |
| 1162 | ``fobj_pgoffs`` holds the content. A ``struct tee_pager_area`` can only use |
| 1163 | ``struct fobj`` and one ``struct pgt`` (translation table) so memory ranges |
| 1164 | spanning multiple fobjs or pgts are split into multiple areas. |
| 1165 | |
| 1166 | ``struct fobj`` |
| 1167 | --------------- |
| 1168 | This is a polymorph object, using different implmentations depending on how |
| 1169 | it's initialized. It's defines as: |
| 1170 | |
| 1171 | .. code-block:: c |
| 1172 | |
| 1173 | struct fobj_ops { |
| 1174 | void (*free)(struct fobj *fobj); |
| 1175 | TEE_Result (*load_page)(struct fobj *fobj, unsigned int page_idx, |
| 1176 | void *va); |
| 1177 | TEE_Result (*save_page)(struct fobj *fobj, unsigned int page_idx, |
| 1178 | const void *va); |
| 1179 | }; |
| 1180 | |
| 1181 | struct fobj { |
| 1182 | const struct fobj_ops *ops; |
| 1183 | unsigned int num_pages; |
| 1184 | struct refcount refc; |
| 1185 | struct tee_pager_area_head areas; |
| 1186 | }; |
| 1187 | |
| 1188 | :``num_pages``: Tells how many pages this ``fobj`` covers. |
| 1189 | :``refc``: A reference counter, everyone referring to a ``fobj`` need to |
| 1190 | increase and decrease this as needed. |
| 1191 | :``areas``: A list of areas using this ``fobj``, traversed when making |
| 1192 | a virtual page unavailable. |
| 1193 | |
| 1194 | ``struct tee_pager_pmem`` |
| 1195 | ------------------------- |
| 1196 | This structure represents a physical page. It's defined as: |
| 1197 | |
| 1198 | .. code-block:: c |
| 1199 | |
| 1200 | struct tee_pager_pmem { |
| 1201 | unsigned int flags; |
| 1202 | unsigned int fobj_pgidx; |
| 1203 | struct fobj *fobj; |
| 1204 | void *va_alias; |
| 1205 | TAILQ_ENTRY(tee_pager_pmem) link; |
| 1206 | }; |
| 1207 | |
| 1208 | :``PMEM_FLAG_DIRTY``: Bit is set in ``flags`` when the page is mapped |
| 1209 | read/write at at least one location. |
| 1210 | :``PMEM_FLAG_HIDDEN``: Bit is set in ``flags`` when the page is hidden, that |
| 1211 | is, not accessible anywhere. |
| 1212 | :``fobj_pgidx``: The page at this index in the ``fobj`` is used in this |
| 1213 | physical page. |
| 1214 | :``fobj``: The ``fobj`` backing this page. |
| 1215 | :``va_alias``: Virtual address where this physical page is updated |
| 1216 | when loading it from backing store or when writing it |
| 1217 | back. |
| 1218 | |
| 1219 | All ``struct tee_pager_pmem`` are stored either in the global list |
| 1220 | ``tee_pager_pmem_head`` or in ``tee_pager_lock_pmem_head``. The latter is |
| 1221 | used by pages which are mapped and then locked in memory on demand. The |
| 1222 | pages are returned back to ``tee_pager_pmem_head`` when the pages are |
| 1223 | exlicitly released with a call to ``tee_pager_release_phys()``. |
| 1224 | |
| 1225 | A physical page can be used by more than one ``tee_pager_area`` |
| 1226 | simultaneously. This is also know as shared secure memory and will appear |
| 1227 | as such for both read-only and read-write mappings. |
| 1228 | |
| 1229 | When a page is hidden it's unmapped from all translation tables and the |
| 1230 | ``PMEM_FLAG_HIDDEN`` bit is set, but kept in memory. When a physical page |
| 1231 | is released it's also unmapped from all translation tables and it's content |
| 1232 | is written back to storage, then the ``fobj`` field is set to ``NULL`` to |
| 1233 | note the physical page as unused. |
| 1234 | |
| 1235 | Note that when ``struct tee_pager_pmem`` references a ``fobj`` it doesn't |
| 1236 | update the reference counter since it's already guaranteed to be available |
| 1237 | due the ``struct tee_pager_area`` which must reference the ``fobj`` too. |
| 1238 | |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1239 | Paging of user TA |
| 1240 | ================= |
| 1241 | Paging of user TAs can optionally be enabled with ``CFG_PAGED_USER_TA=y``. |
| 1242 | Paging of user TAs is analogous to paging of OP-TEE kernel parts but with a few |
| 1243 | differences: |
| 1244 | |
| 1245 | - Read/write pages are paged in addition to read-only pages |
| 1246 | - Page tables are managed dynamically |
| 1247 | |
| 1248 | ``tee_pager_add_uta_area(...)`` is used to setup initial read/write mapping |
| 1249 | needed when populating the TA. When the TA is fully populated and relocated |
| 1250 | ``tee_pager_set_uta_area_attr(...)`` changes the mapping of the area to strict |
| 1251 | permissions used when the TA is running. |
| 1252 | |
Jens Wiklander | aecf441 | 2019-02-26 12:33:14 +0100 | [diff] [blame] | 1253 | Paging shared secure memory |
| 1254 | --------------------------- |
| 1255 | Shared secure memory is achieved by letting several ``tee_pager_area`` |
| 1256 | using the same backing ``fobj``. When a ``tee_pager_area`` is allocated and |
| 1257 | assigned a ``fobj`` it's also added to a list for ``tee_pager_areas`` using |
| 1258 | this ``fobj``. This helps when a physical page is released. |
| 1259 | |
| 1260 | When a fault occurs first a matching ``tee_pager_area`` is located. Then |
| 1261 | ``tee_pager_pmem_head`` is searched to see if a physical page already holds |
| 1262 | the page of the ``fobj`` needed. If so the ``pgt`` is updated to map the |
| 1263 | physical page at the appropriate locatation. If no physical page was holding |
| 1264 | the page a new physical page is allocated, initialized and finally mapped. |
| 1265 | |
| 1266 | In order to make as few updates to mappings as possible changes to less |
| 1267 | restricted, no access -> read-only or read-only to read-write, is done only |
| 1268 | for the virtual address was used when the page fault occurred. Changes in |
| 1269 | the other direction has to be done in all translation tables used to map |
| 1270 | the physical page. |
| 1271 | |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1272 | ---- |
| 1273 | |
| 1274 | .. _stacks: |
| 1275 | |
| 1276 | Stacks |
| 1277 | ****** |
| 1278 | Different stacks are used during different stages. The stacks are: |
| 1279 | |
| 1280 | - **Secure monitor stack** (128 bytes), bound to the CPU. Only available if |
| 1281 | OP-TEE is compiled with a secure monitor always the case if the target is |
| 1282 | Armv7-A but never for Armv8-A. |
| 1283 | |
| 1284 | - **Temp stack** (small ~1KB), bound to the CPU. Used when transitioning |
| 1285 | from one state to another. Interrupts are always disabled when using this |
| 1286 | stack, aborts are fatal when using the temp stack. |
| 1287 | |
| 1288 | - **Abort stack** (medium ~2KB), bound to the CPU. Used when trapping a data |
| 1289 | or pre-fetch abort. Aborts from user space are never fatal the TA is only |
| 1290 | killed. Aborts from kernel mode are used by the pager to do the demand |
| 1291 | paging, if pager is disabled all kernel mode aborts are fatal. |
| 1292 | |
| 1293 | - **Thread stack** (large ~8KB), not bound to the CPU instead used by the |
| 1294 | current thread/task. Interrupts are usually enabled when using this stack. |
| 1295 | |
| 1296 | Notes for Armv7-A/AArch32 |
| 1297 | .. list-table:: |
| 1298 | :header-rows: 1 |
| 1299 | :widths: 1 5 |
| 1300 | |
| 1301 | * - Stack |
| 1302 | - Comment |
| 1303 | |
| 1304 | * - Temp |
| 1305 | - Assigned to ``SP_SVC`` during entry/exit, always assigned to |
| 1306 | ``SP_IRQ`` and ``SP_FIQ`` |
| 1307 | |
| 1308 | * - Abort |
| 1309 | - Always assigned to ``SP_ABT`` |
| 1310 | |
| 1311 | * - Thread |
| 1312 | - Assigned to ``SP_SVC`` while a thread is active |
| 1313 | |
| 1314 | Notes for AArch64 |
| 1315 | There are only two stack pointers, ``SP_EL1`` and ``SP_EL0``, available for |
| 1316 | OP-TEE in AArch64. When an exception is received stack pointer is always |
| 1317 | ``SP_EL1`` which is used temporarily while assigning an appropriate stack |
| 1318 | pointer for ``SP_EL0``. ``SP_EL1`` is always assigned the value of |
| 1319 | ``thread_core_local[cpu_id]``. This structure has some spare space for |
| 1320 | temporary storage of registers and also keeps the relevant stack pointers. |
| 1321 | In general when we talk about assigning a stack pointer to the CPU below we |
| 1322 | mean ``SP_EL0``. |
| 1323 | |
| 1324 | Boot |
| 1325 | ==== |
| 1326 | During early boot the CPU is configured with the temp stack which is used until |
| 1327 | OP-TEE exits to normal world the first time. |
| 1328 | |
| 1329 | Notes for AArch64 |
| 1330 | ``SPSEL`` is always ``0`` on entry/exit to have ``SP_EL0`` acting as stack |
| 1331 | pointer. |
| 1332 | |
| 1333 | Normal entry |
| 1334 | ============ |
| 1335 | Each time OP-TEE is entered from normal world the temp stack is used as the |
| 1336 | initial stack. For fast calls, this is the only stack used. For normal calls an |
| 1337 | empty thread slot is selected and the CPU switches to that stack. |
| 1338 | |
| 1339 | Normal exit |
| 1340 | =========== |
| 1341 | Normal exit occurs when a thread has finished its task and the thread is freed. |
| 1342 | When the main thread function, ``tee_entry_std(...)``, returns interrupts are |
| 1343 | disabled and the CPU switches to the temp stack instead. The thread is freed and |
| 1344 | OP-TEE exits to normal world. |
| 1345 | |
| 1346 | RPC exit |
| 1347 | ======== |
| 1348 | RPC exit occurs when OP-TEE need some service from normal world. RPC can |
| 1349 | currently only be performed with a thread is in running state. RPC is initiated |
| 1350 | with a call to ``thread_rpc(...)`` which saves the state in a way that when the |
| 1351 | thread is restored it will continue at the next instruction as if this function |
| 1352 | did a normal return. CPU switches to use the temp stack before returning to |
| 1353 | normal world. |
| 1354 | |
| 1355 | Foreign interrupt exit |
| 1356 | ====================== |
| 1357 | Foreign interrupt exit occurs when OP-TEE receives a foreign interrupt. For Arm |
| 1358 | GICv2 mode, foreign interrupt is sent as IRQ which is always handled in normal |
| 1359 | world. Foreign interrupt exit is similar to RPC exit but it is |
| 1360 | ``thread_irq_handler(...)`` and ``elx_irq(...)`` (respectively for |
| 1361 | Armv7-A/Aarch32 and for Aarch64) that saves the thread state instead. The thread |
| 1362 | is resumed in the same way though. For Arm GICv3 mode, foreign interrupt is sent |
| 1363 | as FIQ which could be handled by either secure world (EL3 in AArch64) or normal |
| 1364 | world. This mode is not supported yet. |
| 1365 | |
| 1366 | Notes for Armv7-A/AArch32 |
| 1367 | SP_IRQ is initialized to temp stack instead of a separate stack. Prior to |
| 1368 | exiting to normal world CPU state is changed to SVC and temp stack is |
| 1369 | selected. |
| 1370 | |
| 1371 | Notes for AArch64 |
| 1372 | ``SP_EL0`` is assigned temp stack and is selected during IRQ processing. The |
| 1373 | original ``SP_EL0`` is saved in the thread context to be restored when |
| 1374 | resuming. |
| 1375 | |
| 1376 | Resume entry |
| 1377 | ============ |
| 1378 | OP-TEE is entered using the temp stack in the same way as for normal entry. The |
| 1379 | thread to resume is looked up and the state is restored to resume execution. The |
| 1380 | procedure to resume from an RPC exit or an foreign interrupt exit is exactly the |
| 1381 | same. |
| 1382 | |
| 1383 | Syscall |
| 1384 | ======= |
| 1385 | Syscall's are executed using the thread stack. |
| 1386 | |
| 1387 | Notes for Armv7-A/AArch32 |
| 1388 | Nothing special ``SP_SVC`` is already set with thread stack. |
| 1389 | |
| 1390 | Notes for syscall AArch64 |
| 1391 | Early in the exception processing the original ``SP_EL0`` is saved in |
| 1392 | ``struct thread_svc_regs`` in case the TA is executed in AArch64. Current |
| 1393 | thread stack is assigned to ``SP_EL0`` which is then selected. When |
| 1394 | returning ``SP_EL0`` is assigned what is in ``struct thread_svc_regs``. This |
| 1395 | allows ``tee_svc_sys_return_helper(...)`` having the syscall exception |
| 1396 | handler return directly to ``thread_unwind_user_mode(...)``. |
| 1397 | |
| 1398 | ---- |
| 1399 | |
| 1400 | .. _shared_memory: |
| 1401 | |
| 1402 | Shared Memory |
| 1403 | ************* |
| 1404 | Shared Memory is a block of memory that is shared between the non-secure and the |
| 1405 | secure world. It is used to transfer data between both worlds. |
| 1406 | |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1407 | The shared memory is allocated and managed by the non-secure world, i.e. the |
| 1408 | Linux OP-TEE driver. Secure world only considers the individual shared buffers, |
| 1409 | not their pool. Each shared memory is referenced with associated attributes: |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1410 | |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1411 | - Buffer start address and byte size, |
| 1412 | - Cache attributes of the shared memory buffer, |
| 1413 | - List of chunks if mapped from noncontiguous pages. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1414 | |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1415 | Shared memory buffer references manipulated must fit inside one of the |
| 1416 | shared memory areas known from the OP-TEE core. OP-TEE supports two kinds |
Jerome Forissier | 7fa91cf | 2020-07-30 16:03:59 +0200 | [diff] [blame] | 1417 | of shared memory areas: an area for contiguous buffers and an area for |
| 1418 | noncontiguous buffers. At least one has to be enabled. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1419 | |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1420 | Contiguous shared buffers |
| 1421 | ========================= |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1422 | Configuration directives ``CFG_SHMEM_START`` and ``CFG_SHMEM_SIZE`` |
| 1423 | define a share memory area where shared memory buffers are contiguous. |
| 1424 | Generic memory layout registers it as the ``MEM_AREA_NSEC_SHM`` memory area. |
| 1425 | |
| 1426 | The non-secure world issues ``OPTEE_SMC_GET_SHM_CONFIG`` to retrieve contiguous |
| 1427 | shared memory area configuration: |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1428 | |
| 1429 | - Physical address of the start of the pool |
| 1430 | - Size of the pool |
| 1431 | - Whether or not the memory is cached |
| 1432 | |
Jens Wiklander | a70d2f4 | 2019-04-25 12:40:49 +0200 | [diff] [blame] | 1433 | Contiguous shared memory (also known as static or reserved shared memory) |
| 1434 | is enabled with the configuration flag ``CFG_CORE_RESERVED_SHM=y``. |
| 1435 | |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1436 | Noncontiguous shared buffers |
| 1437 | ============================ |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1438 | To benefit from noncontiguous shared memory buffers, secure world register |
| 1439 | dynamic shared memory areas and non-secure world must register noncontiguous |
| 1440 | buffers prior to referring to them using the OP-TEE API. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1441 | |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1442 | The OP-TEE core generic boot sequence discovers dynamic shared areas from the |
| 1443 | device tree and/or areas explicitly registered by the platform. |
| 1444 | |
| 1445 | Non-secure side needs to register buffers as 4kByte chunks lists into OP-TEE |
| 1446 | core using the ``OPTEE_MSG_CMD_REGISTER_SHM`` API prior referencing to them |
| 1447 | using the OP-TEE invocation API. |
| 1448 | |
Jens Wiklander | a70d2f4 | 2019-04-25 12:40:49 +0200 | [diff] [blame] | 1449 | Noncontiguous shared memory (also known as dynamic shared memory) is |
| 1450 | enabled with the configuration flag ``CFG_CORE_DYN_SHM=y``. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1451 | |
Jerome Forissier | 7fa91cf | 2020-07-30 16:03:59 +0200 | [diff] [blame] | 1452 | For performance reasons, the TEE Client Library (``libteec``) uses |
| 1453 | noncontiguous shared memory when available since it avoids copies in some |
| 1454 | situations. |
| 1455 | |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1456 | Shared Memory Chunk Allocation |
| 1457 | ============================== |
| 1458 | It is the Linux kernel driver for OP-TEE that is responsible for allocating |
| 1459 | chunks of shared memory. OP-TEE linux kernel driver relies on linux kernel |
| 1460 | generic allocation support (``CONFIG_GENERIC_ALLOCATION``) to allocation/release |
| 1461 | of shared memory physical chunks. OP-TEE linux kernel driver relies on linux |
| 1462 | kernel dma-buf support (``CONFIG_DMA_SHARED_BUFFER``) to track shared memory |
| 1463 | buffers references. |
| 1464 | |
Jens Wiklander | 5b14517 | 2021-06-30 10:15:41 +0200 | [diff] [blame] | 1465 | Registering shared memory |
| 1466 | ========================= |
| 1467 | |
| 1468 | Only dynamic or physically non-contiguous shared memory needs to be |
| 1469 | registered. Static or physically contiguous shared memory is already known |
| 1470 | to OP-TEE OS. |
| 1471 | |
| 1472 | SMC based OP-TEE MSG ABI |
| 1473 | ------------------------ |
| 1474 | |
| 1475 | With the SMC based OP-TEE MSG ABI there are a few exceptions where memory |
| 1476 | doesn't need to be shared before it can be accessed from OP-TEE OS. These |
| 1477 | are: |
| 1478 | |
| 1479 | 1. When issuing the SMC ``OPTEE_SMC_CALL_WITH_ARG`` where the physical |
| 1480 | address of the supplied ``struct optee_msg_arg`` is passed in one of the |
| 1481 | registers. |
| 1482 | 2. When issuing the SMC ``OPTEE_SMC_CALL_RETURN_FROM_RPC`` as a return from |
| 1483 | the request ``OPTEE_SMC_RETURN_RPC_ALLOC`` to allocate memory. This RPC |
| 1484 | return is combined with an implicit registration of shared memory. The |
| 1485 | registration is ended with a ``OPTEE_SMC_RETURN_RPC_FREE`` request. |
| 1486 | |
| 1487 | .. uml:: |
| 1488 | :align: center |
| 1489 | :caption: Register shared memory example |
| 1490 | |
| 1491 | participant "Normal World\nOS Kernel" as ns |
| 1492 | participant "Secure World\nOP-TEE OS" as sec |
| 1493 | |
| 1494 | ns -> sec : OPTEE_MSG_CMD_REGISTER_SHM(Cookie, memory) |
| 1495 | sec -> sec : Register shared memory passed |
| 1496 | sec -> ns : Return |
| 1497 | |
| 1498 | .. uml:: |
| 1499 | :align: center |
| 1500 | :caption: Unregister shared memory example |
| 1501 | |
| 1502 | participant "Normal World\nOS Kernel" as ns |
| 1503 | participant "Secure World\nOP-TEE OS" as sec |
| 1504 | |
| 1505 | ns -> sec : OPTEE_MSG_CMD_UNREGISTER_SHM(Cookie) |
| 1506 | sec -> sec : Unregister shared memory |
| 1507 | sec -> ns : Return |
| 1508 | |
| 1509 | FF-A based OP-TEE MSG ABI |
| 1510 | ------------------------- |
| 1511 | |
| 1512 | With the FF-A based OP-TEE MSG ABI memory must always be registered before |
| 1513 | it can be used by OP-TEE OS. This case can potentially also involve another |
| 1514 | component in secure world, SPMC at ``S-EL2`` a secure hypervisor which |
| 1515 | controls which memory OP-TEE OS can see or use. |
| 1516 | |
| 1517 | In the case where there are no SPMC at ``S-EL2`` OP-TEE OS will take care |
| 1518 | of that part of the communication with normal world. This means that for |
| 1519 | normal world communication with OP-TEE OS is the same regardless of the |
| 1520 | presence of a secure hypervisor. |
| 1521 | |
| 1522 | Registration of shared memory is a two step procedure. It's first |
| 1523 | registered with a call to the SPMC which returns a cookie or global memory |
| 1524 | handle. This cookie is later used when calling OP-TEE OS, if the cookie |
| 1525 | isn't already known to OP-TEE OS it will ask the SPMC to make the memory |
| 1526 | available. This lazy second step is a way of saving an extra round trip to |
| 1527 | secure world. |
| 1528 | |
| 1529 | .. uml:: |
| 1530 | :align: center |
| 1531 | :caption: Register shared memory example |
| 1532 | |
| 1533 | participant "Normal World\nOS Kernel" as ns |
| 1534 | participant "Secure World\nSPMC" as spmc |
| 1535 | participant "Secure World\nOP-TEE OS" as sec |
| 1536 | |
| 1537 | ns -> spmc : FFA_MEM_SHARE(memory) |
| 1538 | spmc -> spmc : Register shared memory passed |
| 1539 | spmc -> ns : Return cookie |
| 1540 | |
| 1541 | .. uml:: |
| 1542 | :align: center |
| 1543 | :caption: Calling OP-TEE OS with shared memory |
| 1544 | |
| 1545 | participant "Normal World\nOS Kernel" as ns |
| 1546 | participant "Secure World\nSPMC" as spmc |
| 1547 | participant "Secure World\nOP-TEE OS" as sec |
| 1548 | |
| 1549 | ns -> sec: OPTEE_FFA_YIELDING_CALL_WITH_ARG(cookie) |
| 1550 | alt cookie not known |
| 1551 | sec -> spmc : FFA_MEM_RETRIEVE_REQ(cookie) |
| 1552 | spmc -> sec : Return memory description |
| 1553 | sec -> sec : Register shared memory |
| 1554 | end |
| 1555 | sec -> sec : Process the yielding call |
| 1556 | sec -> ns : Return |
| 1557 | |
| 1558 | Unregistration of shared memory is also done in two steps. First with a |
| 1559 | call to OP-TEE and then with a call to the SPMC. If the lazy second |
| 1560 | step of shared memory has not been done, then OP-TEE OS doesn't need |
| 1561 | to interact with the SPMC. |
| 1562 | |
| 1563 | .. uml:: |
| 1564 | :align: center |
| 1565 | :caption: Unregister shared memroy |
| 1566 | |
| 1567 | participant "Normal World\nOS Kernel" as ns |
| 1568 | participant "Secure World\nSPMC" as spmc |
| 1569 | participant "Secure World\nOP-TEE OS" as sec |
| 1570 | |
| 1571 | ns -> sec: OPTEE_FFA_UNREGISTER_SHM(cookie) |
| 1572 | alt cookie known |
| 1573 | sec -> sec : Unregister shared memory |
| 1574 | sec -> spmc : FFA_MEM_RELINQUISH(cookie) |
| 1575 | spmc -> sec : Return |
| 1576 | end |
| 1577 | sec -> ns : Return |
| 1578 | |
| 1579 | ns -> spmc : FFA_MEM_RECLAIM(cookie) |
| 1580 | spmc -> spmc : Unregister shared memory |
| 1581 | spmc -> ns : Return |
| 1582 | |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1583 | Using shared memory |
| 1584 | =================== |
| 1585 | From the Client Application |
| 1586 | The client application can ask for shared memory allocation using the |
| 1587 | GlobalPlatform Client API function ``TEEC_AllocateSharedMemory(...)``. The |
Etienne Carriere | 9c60025 | 2019-03-11 11:01:48 +0100 | [diff] [blame] | 1588 | client application can also register a memory through the GlobalPlatform |
| 1589 | Client API function ``TEEC_RegisterSharedMemory(...)``. The shared memory |
| 1590 | reference can then be used as parameter when invoking a trusted application. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1591 | |
| 1592 | From the Linux Driver |
| 1593 | Occasionally the Linux kernel driver needs to allocate shared memory for the |
| 1594 | communication with secure world, for example when using buffers of type |
| 1595 | ``TEEC_TempMemoryReference``. |
| 1596 | |
| 1597 | From OP-TEE core |
| 1598 | In case OP-TEE core needs information from TEE supplicant (dynamic TA |
| 1599 | loading, REE time request,...), shared memory must be allocated. Allocation |
| 1600 | depends on the use case. OP-TEE core asks for the following shared memory |
| 1601 | allocation: |
| 1602 | |
| 1603 | - ``optee_msg_arg`` structure, used to pass the arguments to the |
| 1604 | non-secure world, where the allocation will be done by sending a |
| 1605 | ``OPTEE_SMC_RPC_FUNC_ALLOC`` message. |
| 1606 | |
| 1607 | - In some cases, a payload might be needed for storing the result from |
| 1608 | TEE supplicant, for example when loading a Trusted Application. This |
| 1609 | type of allocation will be done by sending the message |
| 1610 | ``OPTEE_MSG_RPC_CMD_SHM_ALLOC(OPTEE_MSG_RPC_SHM_TYPE_APPL,...)``, |
| 1611 | which then will return: |
| 1612 | |
| 1613 | - the physical address of the shared memory |
| 1614 | - a handle to the memory, that later on will be used later on when |
| 1615 | freeing this memory. |
| 1616 | |
| 1617 | From TEE Supplicant |
| 1618 | TEE supplicant is also working with shared memory, used to exchange data |
| 1619 | between normal and secure worlds. TEE supplicant receives a memory address |
| 1620 | from the OP-TEE core, used to store the data. This is for example the case |
| 1621 | when a Trusted Application is loaded. In this case, TEE supplicant must |
| 1622 | register the provided shared memory in the same way a client application |
| 1623 | would do, involving the Linux driver. |
| 1624 | |
| 1625 | ---- |
| 1626 | |
| 1627 | .. _smc: |
| 1628 | |
| 1629 | SMC |
| 1630 | *** |
| 1631 | SMC Interface |
| 1632 | ============= |
| 1633 | OP-TEE's SMC interface is defined in two levels using optee_smc.h_ and |
| 1634 | optee_msg.h_. The former file defines SMC identifiers and what is passed in the |
| 1635 | registers for each SMC. The latter file defines the OP-TEE Message protocol |
| 1636 | which is not restricted to only SMC even if that currently is the only option |
| 1637 | available. |
| 1638 | |
| 1639 | SMC communication |
| 1640 | ================= |
| 1641 | The main structure used for the SMC communication is defined in ``struct |
| 1642 | optee_msg_arg`` (in optee_msg.h_). If we are looking into the source code, we |
| 1643 | could see that communication mainly is achieved using ``optee_msg_arg`` and |
| 1644 | ``thread_smc_args`` (in thread.h_), where ``optee_msg_arg`` could be seen as the |
| 1645 | main structure. What will happen is that the :ref:`linux_kernel` driver will get |
| 1646 | the parameters either from :ref:`optee_client` or directly from an internal |
| 1647 | service in Linux kernel. The TEE driver will populate the struct |
| 1648 | ``optee_msg_arg`` with the parameters plus some additional bookkeeping |
| 1649 | information. Parameters for the SMC are passed in registers 1 to 7, register 0 |
| 1650 | holds the SMC id which among other things tells whether it is a standard or a |
| 1651 | fast call. |
| 1652 | |
| 1653 | ---- |
| 1654 | |
| 1655 | .. _thread_handling: |
| 1656 | |
| 1657 | Thread handling |
| 1658 | *************** |
| 1659 | OP-TEE core uses a couple of threads to be able to support running jobs in |
| 1660 | parallel (not fully enabled!). There are handlers for different purposes. In |
| 1661 | thread.c_ you will find a function called ``thread_init_primary(...)`` which |
| 1662 | assigns ``init_handlers`` (functions) that should be called when OP-TEE core |
| 1663 | receives standard or fast calls, FIQ and PSCI calls. There are default handlers |
| 1664 | for these services, but the platform can decide if they want to implement their |
| 1665 | own platform specific handlers instead. |
| 1666 | |
| 1667 | Synchronization primitives |
| 1668 | ========================== |
| 1669 | OP-TEE has three primitives for synchronization of threads and CPUs: |
| 1670 | *spin-lock*, *mutex*, and *condvar*. |
| 1671 | |
| 1672 | Spin-lock |
| 1673 | A spin-lock is represented as an ``unsigned int``. This is the most |
| 1674 | primitive lock. Interrupts should be disabled before attempting to take a |
| 1675 | spin-lock and should remain disabled until the lock is released. A spin-lock |
| 1676 | is initialized with ``SPINLOCK_UNLOCK``. |
| 1677 | |
| 1678 | .. list-table:: Spin lock functions |
| 1679 | :header-rows: 1 |
| 1680 | :widths: 1 5 |
| 1681 | |
| 1682 | * - Function |
| 1683 | - Purpose |
| 1684 | |
| 1685 | * - ``cpu_spin_lock(...)`` |
| 1686 | - Locks a spin-lock |
| 1687 | |
| 1688 | * - ``cpu_spin_trylock(...)`` |
| 1689 | - Locks a spin-lock if unlocked and returns ``0`` else the spin-lock |
| 1690 | is unchanged and the function returns ``!0`` |
| 1691 | |
| 1692 | * - ``cpu_spin_unlock(...)`` |
| 1693 | - Unlocks a spin-lock |
| 1694 | |
| 1695 | Mutex |
| 1696 | A mutex is represented by ``struct mutex``. A mutex can be locked and |
| 1697 | unlocked with interrupts enabled or disabled, but only from a normal thread. |
| 1698 | A mutex cannot be used in an interrupt handler, abort handler or before a |
| 1699 | thread has been selected for the CPU. A mutex is initialized with either |
| 1700 | ``MUTEX_INITIALIZER`` or ``mutex_init(...)``. |
| 1701 | |
| 1702 | .. list-table:: Mutex functions |
| 1703 | :header-rows: 1 |
| 1704 | :widths: 1 5 |
| 1705 | |
| 1706 | * - Function |
| 1707 | - Purpose |
| 1708 | |
| 1709 | * - ``mutex_lock(...)`` |
| 1710 | - Locks a mutex. If the mutex is unlocked this is a fast operation, |
| 1711 | else the function issues an RPC to wait in normal world. |
| 1712 | |
| 1713 | * - ``mutex_unlock(...)`` |
| 1714 | - Unlocks a mutex. If there is no waiters this is a fast operation, |
| 1715 | else the function issues an RPC to wake up a waiter in normal world. |
| 1716 | |
| 1717 | * - ``mutex_trylock(...)`` |
| 1718 | - Locks a mutex if unlocked and returns ``true`` else the mutex is |
| 1719 | unchanged and the function returns ``false``. |
| 1720 | |
| 1721 | * - ``mutex_destroy(...)`` |
| 1722 | - Asserts that the mutex is unlocked and there is no waiters, after |
| 1723 | this the memory used by the mutex can be freed. |
| 1724 | |
| 1725 | When a mutex is locked it is owned by the thread calling ``mutex_lock(...)`` |
| 1726 | or ``mutex_trylock(...)``, the mutex may only be unlocked by the thread |
| 1727 | owning the mutex. A thread should not exit to TA user space when holding a |
| 1728 | mutex. |
| 1729 | |
| 1730 | Condvar |
| 1731 | A condvar is represented by ``struct condvar``. A condvar is similar to a |
| 1732 | ``pthread_condvar_t`` in the pthreads standard, only less advanced. |
| 1733 | Condition variables are used to wait for some condition to be fulfilled and |
| 1734 | are always used together a mutex. Once a condition variable has been used |
| 1735 | together with a certain mutex, it must only be used with that mutex until |
| 1736 | destroyed. A condvar is initialized with ``CONDVAR_INITIALIZER`` or |
| 1737 | ``condvar_init(...)``. |
| 1738 | |
| 1739 | .. list-table:: Condvar functions |
| 1740 | :header-rows: 1 |
| 1741 | :widths: 1 5 |
| 1742 | |
| 1743 | * - Function |
| 1744 | - Purpose |
| 1745 | |
| 1746 | * - ``condvar_wait(...)`` |
| 1747 | - Atomically unlocks the supplied mutex and waits in normal world via |
| 1748 | an RPC for the condition variable to be signaled, when the function |
| 1749 | returns the mutex is locked again. |
| 1750 | |
| 1751 | * - ``condvar_signal(...)`` |
| 1752 | - Wakes up one waiter of the condition variable (waiting in |
| 1753 | ``condvar_wait(...)``). |
| 1754 | |
| 1755 | * - ``condvar_broadcast(...)`` |
| 1756 | - Wake up all waiters of the condition variable. |
| 1757 | |
| 1758 | The caller of ``condvar_signal(...)`` or ``condvar_broadcast(...)`` should |
| 1759 | hold the mutex associated with the condition variable to guarantee that a |
| 1760 | waiter does not miss the signal. |
| 1761 | |
| 1762 | .. _core/arch/arm/kernel/thread.c: https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/kernel/thread.c |
| 1763 | .. _optee_msg.h: https://github.com/OP-TEE/optee_os/blob/master/core/include/optee_msg.h |
| 1764 | .. _optee_smc.h: https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/include/sm/optee_smc.h |
| 1765 | .. _thread.c: https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/kernel/thread.c |
| 1766 | .. _thread.h: https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/include/kernel/thread.h |
Etienne Carriere | eda1e0f | 2023-05-07 17:29:28 +0200 | [diff] [blame] | 1767 | .. _interrupt.h: https://github.com/OP-TEE/optee_os/blob/master/core/include/kernel/interrupt.h |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1768 | |
| 1769 | .. _ARM_DEN0028A_SMC_Calling_Convention: http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf |
| 1770 | .. _Cortex-A53 TRM: http://infocenter.arm.com/help/topic/com.arm.doc.ddi0500j/DDI0500J_cortex_a53_trm.pdf |
| 1771 | .. _drivers/tee/optee: https://github.com/torvalds/linux/tree/master/drivers/tee/optee |
| 1772 | .. _Trusted Firmware A: https://github.com/ARM-software/arm-trusted-firmware |