Mate Toth-Pal | b4d8fab | 2019-10-28 10:20:43 +0100 | [diff] [blame] | 1 | ############################# |
| 2 | Non-Secure Interrupt Handling |
| 3 | ############################# |
| 4 | |
| 5 | :Author: Mate Toth-Pal |
| 6 | :Organization: Arm Limited |
| 7 | :Contact: Mate Toth-Pal <mate.toth-pal@arm.com> |
| 8 | :Status: Accepted |
| 9 | |
| 10 | |
| 11 | ***** |
| 12 | Terms |
| 13 | ***** |
| 14 | |
| 15 | ========== ================================================ |
| 16 | Term Meaning |
| 17 | ========== ================================================ |
| 18 | AIRCR Application Interrupt and Reset Control Register |
| 19 | AIRCR.PRIS PRIoritize Secure exceptions |
| 20 | ISR Interrupt Service Routine |
| 21 | NS Non-Secure |
| 22 | NSPM Non-Secure Partition Manager |
| 23 | SAU Security Attribution Unit |
| 24 | SPM Secure Partition Manager |
| 25 | TF-M Trusted Firmware-M |
| 26 | ========== ================================================ |
| 27 | |
| 28 | ************ |
| 29 | Introduction |
| 30 | ************ |
| 31 | |
| 32 | In the current design it is possible to use Non-secure interrupts, however the |
| 33 | Non-secure interrupts cannot pre-empt Secure service execution. TF-M core |
| 34 | achieves this by making the following configurations: |
| 35 | |
| 36 | 1. ``AIRCR.PRIS`` is set to 1 during TF-M core initialisation. This |
| 37 | de-prioritizes Non-secure exceptions compared to Secure exceptions, so that |
Edison Ai | f7990c8 | 2020-07-16 18:31:48 +0800 | [diff] [blame] | 38 | they cannot interrupt Secure Handler mode. the ``AIRCR.PRIS`` bit remains set |
Mate Toth-Pal | b4d8fab | 2019-10-28 10:20:43 +0100 | [diff] [blame] | 39 | during TF-M run. The bit is set in the function |
| 40 | |
| 41 | .. code-block:: c |
| 42 | |
Ken Liu | 50e2109 | 2020-10-14 16:42:15 +0800 | [diff] [blame] | 43 | static void tfm_arch_set_secure_exception_priorities(void); |
Mate Toth-Pal | b4d8fab | 2019-10-28 10:20:43 +0100 | [diff] [blame] | 44 | |
| 45 | .. Note:: |
| 46 | |
| 47 | Setting ``AIRCR.PRIS`` in itself doesn't prevent NS interrupts to pre-empt |
| 48 | Secure Thread mode when it runs on normal priority i.e., 256. |
| 49 | |
| 50 | 2. On Secure service entry ``PRIMASK_NS`` is set, to boost the Non-secure |
| 51 | execution priority so that all NS interrupts are masked. This is done in the |
| 52 | ``TFM_NS_EXC_DISABLE()`` macro called from |
| 53 | |
| 54 | .. code-block:: c |
| 55 | |
| 56 | static int32_t tfm_start_partition(struct tfm_sfn_req_s *desc_ptr, uint32_t excReturn) |
| 57 | |
| 58 | .. Note:: |
| 59 | |
| 60 | '2.' is only in Library model. |
| 61 | |
| 62 | In the below chapters a design is proposed to enable Non-secure interrupts to |
| 63 | pre-empt Secure Thread mode. |
| 64 | |
| 65 | ********************************** |
| 66 | Limitations of the proposed design |
| 67 | ********************************** |
| 68 | |
| 69 | Library model |
| 70 | ============= |
| 71 | |
| 72 | The proposed design keeps the Secure lock in place, which means Non-secure code |
| 73 | can do a single Secure service call, all further calls to Secure services will |
| 74 | be rejected until the first Secure service call returns. |
| 75 | |
| 76 | IPC model |
| 77 | ========= |
| 78 | |
| 79 | The PSA client API can only be entered once. All the functions in the client API |
| 80 | (``psa_framework_version``, ``psa_version``, ``psa_connect``, ``psa_call``, |
| 81 | ``psa_close``) are part of the same critical section. |
| 82 | |
| 83 | Non-secure software |
| 84 | =================== |
| 85 | |
| 86 | The Non-secure API functions that wraps the Secure service veneers (either |
| 87 | Library or IPC model) should continue to use the NS locking mechanism currently |
| 88 | implemented by calling ``tfm_ns_lock_dispatch(...)``. |
| 89 | |
| 90 | If any of the Non-secure software decides to bypass the locking mechanism, then |
| 91 | concurrent access of veneer functions is detected by TF-M, and NS software |
| 92 | execution is halted. |
| 93 | |
| 94 | .. Note:: |
| 95 | |
| 96 | This makes denial of service attack possible by a malicious NS application |
| 97 | |
| 98 | *************************************************** |
| 99 | Enabling NS interrupts to pre-empt Secure execution |
| 100 | *************************************************** |
| 101 | |
| 102 | To enable NS interrupts, 2) described in chapter 'Current design' must be turned |
| 103 | off. (For details see implementation notes) |
| 104 | |
| 105 | When a Non-secure interrupt is triggered during Secure code execution, and the |
| 106 | ISR have to be executed based on the priority settings, the hardware saves the |
| 107 | current execution context on the current Secure stack, and clears the general |
| 108 | purpose registers, to prevents data leakage. After that the NS ISR starts |
| 109 | execution. |
| 110 | |
| 111 | When the Non-secure ISR returns with the EXC_RETURN value provided to it in the |
| 112 | link register, the context is fetched from the Secure stack, and the Secure code |
| 113 | continues execution. |
| 114 | |
| 115 | If TF-M is used with a single threaded NS software, the mechanisms provided by |
| 116 | the HW is enough to maintain the consistency of the system, and keep the |
| 117 | secrets. |
| 118 | |
| 119 | However if the NS software is allowed to change execution context during an |
| 120 | interrupt (e.g an NS operating system schedules another thread during a SysTick |
| 121 | interrupt), then the Secure code can return execution to an NS thread, with the |
| 122 | context of a different thread. So extra measures needs to be introduced in TF-M |
| 123 | to prevent this. |
| 124 | |
| 125 | For IPC model |
| 126 | ============= |
| 127 | |
| 128 | In the current implementation there is no locking mechanism on the Secure side |
| 129 | that would prevent the Non-secure code to enter psa_client functions multiple |
| 130 | times. (For the Library model the ``tfm_secure_lock global`` variable is used |
| 131 | for this purpose). Note, that the ``tfm_ns_lock_dispatch(...)`` function that |
| 132 | is used by the NS service API implementations to prevent Secure services to be |
| 133 | called simultaneously can be bypassed by a malicious Non-secure application, so |
| 134 | a Secure side locking mechanism have to be implemented. |
| 135 | |
| 136 | When an NS client calls a PSA client API function, the client ID of the calling |
| 137 | NS context have to be saved, and execution can only return to NS if the current |
| 138 | scheduled NS thread is the one that did the call. |
| 139 | |
| 140 | For Library model |
| 141 | ================= |
| 142 | |
| 143 | As currently there is no scheduling in the Library model, the calls follow each |
| 144 | other just like in an ordinary function call scheme. Then when the original |
| 145 | Secure service that was called from the NS code is about to return, it has to |
| 146 | check for the current NS client ID, and only return if it is the same as the one |
| 147 | saved on Secure service entry from NS. If the ID's don't match, the Secure side |
| 148 | waits so that NS OS can do context switch. |
| 149 | |
| 150 | Common measures |
| 151 | =============== |
| 152 | |
| 153 | Exception priorities |
| 154 | -------------------- |
| 155 | |
| 156 | The priority of the Secure SVC and the Secure faults must be higher than any |
Ken Liu | 50e2109 | 2020-10-14 16:42:15 +0800 | [diff] [blame] | 157 | Secure exception in the system. |
Mate Toth-Pal | b4d8fab | 2019-10-28 10:20:43 +0100 | [diff] [blame] | 158 | |
| 159 | .. note:: |
| 160 | |
| 161 | The priority of PendSV Is set to be the lowest priority Secure interrupt, |
| 162 | but still higher than the maximum possible NS execution priority when |
| 163 | ``AIRCR.PRIS`` is set. |
| 164 | |
| 165 | NSPM |
| 166 | ---- |
| 167 | |
| 168 | If the Non-secure software allows the use of multiple threads, it needs to use |
| 169 | the NSPM feature of TF-M. It is expected, that all the NS context that use |
| 170 | Secure services have a unique client ID, and the other contexts, that don't use |
| 171 | Secure service need to have a client ID that doesn't match with any of the |
| 172 | client IDs of the Secure service calling contexts. |
| 173 | |
| 174 | In other words, for all the *cs(0)*, *cs(1)*, ..., *cs(n)* NS contexts that use |
| 175 | Secure services and for all *cn(0)*, *cn(1)*, ..., *cn(m)* NS contexts that |
| 176 | don't use Secure service (where *n* > 0, *m* >= 0): |
| 177 | |
| 178 | - *cs(i).client_id* != *cs(j).client_id* (where 0 <= *i* < *j* <= *n*) |
| 179 | - *cs(i).client_id* != *cn(j).client_id* (where 0 <= *i* <= *n* and 0 <= *j* |
| 180 | <= *m*) |
| 181 | |
| 182 | Entering from Non-secure to Secure |
| 183 | ---------------------------------- |
| 184 | |
| 185 | The Secure code can be entered through the following gateways: |
| 186 | |
| 187 | 1. NSPM related functions (``TZ_<operation>(...)``, |
| 188 | ``tfm_register_client_id(...)``) |
| 189 | |
| 190 | These functions are expected to be called from Handler mode. The execution |
| 191 | priority, after the execution crosses the security boundary will be the same |
| 192 | as it was during NS execution. This means a malicious Non-secure application, |
| 193 | can set up Non-secure interrupt priorities in a way that it can enter one or |
| 194 | more of the NSPM APIs simultaneously. |
| 195 | |
| 196 | This might leave the NSPM database in an inconsistent state, however if the |
| 197 | attacker has influence over the interrupt priorities, they can gain no |
| 198 | additional privilege by this. |
| 199 | |
| 200 | .. Note:: |
| 201 | The NS software is able to consume the main stack of the Secure software. |
| 202 | The Main Secure stack have to be protected by MSPLIM, to prevent stack |
| 203 | overflow. However a denial of service attack is still possible. |
| 204 | |
| 205 | 2. PSA Client API, Library model service veneers |
| 206 | |
| 207 | When a veneer is called from Non-secure, the Secure code have to check |
| 208 | whether the veneer is only entered by a single NS thread. This can be done by |
| 209 | checking the veneer stack usage. It can only contain the locals of the veneer |
| 210 | implementation. If the veneer has been entered from multiple NS threads, |
| 211 | there is at least one extra context stack frame that was created by the |
| 212 | hardware when the veneer execution had been interrupted by the NS systick. |
| 213 | |
| 214 | ******************** |
| 215 | Implementation notes |
| 216 | ******************** |
| 217 | |
| 218 | IPC model |
| 219 | ========= |
| 220 | |
| 221 | Save NS client ID on Secure service veneer entry |
| 222 | ------------------------------------------------ |
| 223 | |
| 224 | As long as the Secure lock is in place, a single client ID have to be stored, so |
| 225 | it can be done in a global variable. |
| 226 | |
| 227 | The caller client ID can be saved in the function |
| 228 | ``void tfm_psa_ipc_request_handler(uint32_t svc_ctx[])`` depending on the return |
| 229 | value of the PSA API function. (Doesn't execute any Secure service code, only |
Edison Ai | f7990c8 | 2020-07-16 18:31:48 +0800 | [diff] [blame] | 230 | sets signals, and triggers scheduling. If the return value is success, that |
| 231 | means a scheduling is to happen, and a secure service is about to be entered.) |
Mate Toth-Pal | b4d8fab | 2019-10-28 10:20:43 +0100 | [diff] [blame] | 232 | |
| 233 | Check client ID on Secure service return |
| 234 | ---------------------------------------- |
| 235 | |
| 236 | The saved client ID can be compared with the current client ID in the function |
| 237 | ``tfm_core_ns_ipc_request``, after the SVC return. Before doing the comparison, |
| 238 | ``BASEPRI_NS`` must be set to 1. |
| 239 | |
| 240 | The original ``BASEPRI_NS`` value can be stored in a global variable (because of |
| 241 | the single context). |
| 242 | |
| 243 | If the client ID's don't match, ``BASEPRI_NS`` must be reset, WFI to be issued, |
| 244 | and start the checking sequence from the beginning. |
| 245 | |
| 246 | Library model |
| 247 | ============= |
| 248 | |
| 249 | Save NS client ID on Secure veneer entry |
| 250 | ---------------------------------------- |
| 251 | |
| 252 | As long as the Secure lock is in place, only a single client ID have to be |
| 253 | stored, so it can be done in a global variable. |
| 254 | |
| 255 | The caller client ID can be saved in the function |
| 256 | ``uint32_t tfm_core_partition_request_svc_handler(uint32_t *svc_args, uint32_t excReturn)``. |
| 257 | |
| 258 | Check client ID on SP return |
| 259 | ---------------------------- |
| 260 | |
| 261 | The saved client ID can be compared with the current client ID in the function |
| 262 | ``tfm_core_partition_request``, after the ``tfm_core_sfn_request return``. |
| 263 | |
| 264 | If the client ID's don't match, WFI to be issued, and the checking sequence have |
| 265 | to be started from the beginning. |
| 266 | |
| 267 | Common |
| 268 | ====== |
| 269 | |
| 270 | Enforce single NS entry to Secure |
| 271 | --------------------------------- |
| 272 | |
| 273 | On Secure service entry (from the SVC implementation) check that (pseudocode) |
| 274 | |
| 275 | .. code-block:: c |
| 276 | |
| 277 | svc_handler() |
| 278 | { |
| 279 | /* If there are multiple context stacked in veneer stack, hang NSPE */ |
| 280 | expected_sp_top = veneer_stack_addr - |
| 281 | sizeof(svc_state_context) + sizeof(locals); |
| 282 | if (__get_PSP() != expected_sp_top) { |
| 283 | /* Multiple frames are existing, panic */ |
| 284 | panic(); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | ******* |
| 289 | Testing |
| 290 | ******* |
| 291 | |
| 292 | Basic scenario |
| 293 | ============== |
| 294 | |
| 295 | Basic testing of the feature is possible, by adding a new scenario to the |
| 296 | existing IRQ test. The flow of the test would be something like this: |
| 297 | |
| 298 | ============ ===================== =========================================== |
| 299 | IRQ_TEST_1 prepare test scenario Do nothing |
| 300 | CORE_TEST_2 prepare test scenario Do nothing |
| 301 | NS prepare test scenario Initialise and start timer |
| 302 | IRQ_TEST_1 execute test scenario Do nothing |
| 303 | CORE_TEST_2 execute test scenario Busy wait until NS interrupt is |
| 304 | acknowledged (a flag in non Secure data is |
| 305 | set) set flag CORE_TEST_2 waits on |
| 306 | NS execute test scenario Do nothing |
| 307 | ============ ===================== =========================================== |
| 308 | |
Edison Ai | f7990c8 | 2020-07-16 18:31:48 +0800 | [diff] [blame] | 309 | The test is successful if NS execute test scenario returns. |
Mate Toth-Pal | b4d8fab | 2019-10-28 10:20:43 +0100 | [diff] [blame] | 310 | |
| 311 | Advanced scenarios |
| 312 | ================== |
| 313 | |
| 314 | Testing advanced scenarios (that involves NS scheduling during secure execution, |
| 315 | NS interrupting Secure interrupt, Secure interrupting NS interrupt) would |
| 316 | require more advanced test framework and are not covered in this proposal. |
| 317 | |
| 318 | *Copyright (c) 2019-2020, Arm Limited. All rights reserved.* |