1################################ 2Secure Partition Runtime Library 3################################ 4 5:Organization: Arm Limited 6:Contact: tf-m@lists.trustedfirmware.org 7 8********** 9Background 10********** 11Trusted Firmware - M (TF-M) uses a toolchain provided runtime library and 12supervisor calls to easily implement the PSA Firmware Framework (PSA FF) API. 13This working model works well under isolation level 1 since there are no data 14isolation requirements. While TF-M is evolving, this model is not suitable 15because: 16 17 - The high-level isolation requires isolating data but some toolchain library 18 interfaces have their own global data which cannot be shared between the 19 Secure Partitions. 20 - The toolchain libraries are designed without taking security as a core 21 design principle. 22 23A TF-M specific runtime library is needed for the following reasons: 24 25 - Easier evaluation or certification by security standards. 26 - Source code transparency. 27 - Sharing code to save ROM and RAM space for TF-M. 28 29PSA FF specification also describes the requirements of C runtime API for Secure 30Partitions. 31 32This runtime library is named the ``Secure Partition Runtime Library``, and the 33abbreviation is ``SPRTL``. 34 35**************** 36Design Principle 37**************** 38The following requirements are mandatory for SPRTL implementation: 39 40.. important:: 41 - **CODE ONLY** - No read-write data should be introduced into runtime library 42 implementation. 43 - **Thread safe** - All functions are designed with thread-safe consideration. 44 These APIs access caller stack and caller provided memory only. 45 - **Isolation** - Runtime API code is set as executable and read-only in 46 higher isolation levels. 47 - **Security first** - SPRTL is designed for security and it may come with 48 some performance loss. 49 50API Categories 51============== 52Several known types of functions are included in SPRTL: 53 54 - C runtime API. 55 - RoT Service API. 56 - PSA Client and Service API. 57 - [Future expansion, to be detailed later] other secure API. 58 59Security Implementation Requirements 60------------------------------------ 61If ``malloc/realloc/free`` are provided, they must obey additional requirements 62compared to the C standard: newly allocated memory must be initialized to 63ZERO, and freed memory must be wiped immediately in case the block contains 64sensitive data. 65 66The comparison API ('memcmp' e.g.), they should not return immediately when the 67fault case is detected. The implementation should execute in linear time based 68on input to avoid execution timing side channel attack. 69 70The pointer validation needs to be considered. In general, at least the 71'non-NULL' checking is mandatory. A detection for invalid pointer leads to a 72``psa_panic()``. 73 74The following section describes the first 3 API types and the implementation 75requirements. 76 77C Runtime API 78------------- 79PSA FF describes a small set of the C standard library. Part of toolchain 80library API can be used as default if these APIs meet the `Design Principle`_ 81and `Security Implementation Requirements`_. The toolchain 'header' and 'types' 82can be reused to simplify the implementation. 83 84These APIs can take the toolchain provided version, or separately implemented 85in case there are extra requirements: 86 87.. note:: 88 - 'memcpy()/memmove()/memset()' 89 - String API 90 91These APIs are proposed to be implemented with the security consideration 92mentioned in `Security Implementation Requirements`_: 93 94.. note:: 95 - 'memcmp()' 96 - Other comparison API if referenced ('strcmp' e.g.). 97 98The following functions are optional, but if present, they must conform to 99additional `Security Implementation Requirements`_: 100 101.. note:: 102 - 'malloc()/free()/realloc()' 103 - 'assert()/printf()' 104 105The following APIs are coupled with toolchain library much so applying toolchain 106library implementation is recommended: 107 108.. note:: 109 - Division and modulo - arithmetic operations. 110 - Other low level or compiler specific functions (such as 'va_list'). 111 112Besides the APIs mentioned above, the following runtime APIs are required for 113runtime APIs with private runtime context ('malloc' e.g.): 114 115.. note:: 116 - '__sprtmain()' - partition entry runtime wrapper. 117 118RoT Service API 119--------------- 120The description of RoT Service API in PSA FF: 121 122.. note:: 123 Arm recommends that the RoT Service developer also defines an RoT Service API 124 and implementation to encapsulate the use of the IPC protocol, and improve the 125 usability of the service for client firmware. 126 127Part of the RoT Service API have proposed specifications, such as the PSA 128Cryptography API, PSA Storage API, and PSA Attestation API. It is suggested that 129the service developer create documents of their RoT Service API and make them 130publicly available. 131 132The RoT Service API has a large amount and it is the main part of SPRTL. This 133chapter describes the general implementation of the RoT Service API and the 134reason for putting them into SPRTL. 135 136In general, a client uses the PSA Client API to access a secure service. 137For example: 138 139.. code-block:: c 140 141 /* Example, not a real implementation */ 142 caller_status_t psa_example_service(void) 143 { 144 ... 145 handle = psa_connect(SERVICE_SID, SERVICE_VERSION); 146 if (INVALID_HANDLE(handle)) { 147 return INVALID_RETURN; 148 } 149 150 status = psa_call(handle, type, invecs, inlen, outvecs, outlen); 151 152 psa_close(handle); 153 154 return TO_CALLER_STATUS(status); 155 } 156 157This example encapsulates the PSA Client API, and can be provided as a simpler 158and more generic API for clients to call. It is not possible to statically link 159this API to each Secure Partition because of the limited storage space. The 160ideal solution is to put it inside SPRTL and share it to all Secure Partitions. 161This would simplify the caller logic into this: 162 163.. code-block:: c 164 165 if (psa_example_service() != STATUS_SUCCESS) { 166 /* do something */ 167 } 168 169This is the simplest case of encapsulating PSA Client API. If a RoT Service API 170is connect heavy, then, the encapsulation can be changed to include a connection 171handle inside a context data structure. This context data structure type is 172defined in RoT Service headers and the instance is allocated by API caller since 173API implementation does not have private data. 174 175.. note:: 176 - Even the RoT Service APIs are provided in SPRTL for all clients, the SPM 177 performs the access check eventually and decides if the access to service 178 can be processed. 179 - For those RoT Service APIs only get called by a specific client, they can be 180 implemented inside the caller client, instead of putting it into SPRTL. 181 182PSA Client and Service API 183-------------------------- 184Most of the PSA APIs can be called directly with supervisor calls. The only 185special function is ``psa_call``, because it has **6** parameters. This makes 186the supervisor call handler complex because it has to extract the parameters 187from the stack. The definition of psa_call is the following: 188 189.. code-block:: c 190 191 psa_status_t psa_call(psa_handle_t handle, int32_t type, 192 const psa_invec *in_vec, size_t in_len, 193 psa_outvec *out_vec, size_t out_len); 194 195The parameters need to be packed to avoid passing parameters on the stack, and 196the supervisor call needs to unpack the parameters back to **6** for subsequent 197processing. 198 199Privileged Access Supporting 200============================ 201Due to specified API (printf, e.g.) need to access privileged resources, TF-M 202Core needs to provide interface for the resources accessing. The permission 203checking must happen in Core while caller is calling these interface. 204 205Secure Partition Local Storage 206============================== 207There are APIs that need to reference specific partition private data ('malloc' 208references local heap, e.g.), and the APIs reference the data by mechanisms 209other than function parameters. The mechanism in TF-M is called 210'Secure Partition Local Storage'. 211 212A straight way for accessing the local storage is to put the local storage 213pointer in a known position in the stack, but there is a bit of difficulty in 214particular scenarios. 215 216.. note:: 217 - The partition's stack is not fixed-size aligned, using stack address 218 aligning method can not work. 219 - It requires privileged permission to *access* special registers such 220 as `PSPLIMIT`. And Armv6-M and Armv7-M don't have `PSPLIMIT`.` 221 222Another common method is to put the pointer in one shared global variable, and 223the scheduler maintains the value of this variable to point to the running 224partition's local storage in runtime. It does not fully align with SPRTL design 225prerequisites listed above, hence extra settings are required to guarantee the 226isolation boundaries are not broken. 227 228.. important:: 229 - This variable is put inside a dedicated shared region and it can not hold 230 information not belonging to the owner. 231 232And this mechanism has disadvantages: 233 234 - It needs extra maintenance effort from the scheduler and extra resources 235 for containing the variable. 236 237TF-M chooses this common way as the default option for local storage and can 238be expanded to support more methods. 239 240Tooling Support on Partition Entry 241================================== 242PSA FF requires each Secure Partition to have an entry point. For example: 243 244.. code-block:: c 245 246 /* The entry point function must not return. */ 247 void entry_point(void); 248 249Each partition has its own dedicated local_storage for heap tracking and other 250runtime state. The local_storage is designed to be saved at the read-write data area 251of a partition with a specific name. A generic entry point needs to be available 252to get partition local_storage and do initialization before calling into the actual 253partition entry. This generic entry point is defined as '__sprtmain': 254 255.. code-block:: c 256 257 void __sprtmain(void) 258 { 259 /* Get current SP private data from local storage */ 260 struct p_sp_local_storage_t *m = 261 (struct p_sp_local_storage_t *)tfm_sprt_local_storage; 262 263 /* Potential heap init - check later chapter */ 264 if (m->heap_size) { 265 m->heap_instance = tfm_sprt_heap_init(m->heap_sa, m->heap_sz); 266 } 267 268 /* Call thread entry 'entry_point' */ 269 m->thread_entry(); 270 271 /* Back to tell Core end this thread */ 272 SVC(THREAD_EXIT); 273 } 274 275Since SPM is not aware of the '__sprtmain' in SPRTL, it just calls into the 276entry point listed in partition runtime data structure. And the partition writer 277may be not aware of running of '__sprtmain' as the generic wrapper entry, 278tooling support needs to happen to support this magic. Here is an example of 279partition manifest: 280 281.. code-block:: sh 282 283 { 284 "name": "TFM_SP_SERVICE", 285 "type": "PSA-ROT", 286 "priority": "NORMAL", 287 "entry_point": "tfm_service_entry", 288 "stack_size": "0x1800", 289 "heap_size": "0x1000", 290 ... 291 } 292 293Tooling would do manipulation to tell SPM the partition entry as '__sprtmain', 294and TF-M SPM would maintain the local storage at run time. Finally, the 295partition entry point gets called and run, tooling helps on the decoupling 296of SPM and SPRTL implementation. The pseudo code of a tooling result: 297 298.. code-block:: c 299 300 struct partition_t sp1 { 301 .name = "TFM_SP_SERVICE", 302 .type = PSA_ROT, 303 .priority = NORMAL, 304 .id = 0x00000100, 305 .entry_point = __sprtmain, /* Tell SPM entry is '__sprtmain' */ 306 .local_storage = { /* struct sprt_local_storage_t */ 307 .heap_sa = sp1_heap_buf, 308 .heap_sz = sizeof(sp1_heap_buf), 309 .thread_entry = sp1_entry, /* Actual Partition Entry */ 310 .heap_instance = NULL, 311 }, 312 } 313 314Implementation 315============== 316The SPRTL C Runtime sources are put under: 317'$TFM_ROOT/secure_fw/partitions/lib/runtime/' 318 319The output of this folder is a static library named as 'libtfm_sprt.a'. The code 320of 'libtfm_sprt.a' is put into a dedicated section so that a hardware protected 321region can be applied to contain it. 322 323The RoT Service API are put under service interface folder. These APIs are 324marked with the same section attribute where 'libtfm_sprt.a' is put. 325 326The Formatting API - 'printf' and variants 327------------------------------------------ 328The 'printf' and its variants need special parameters passing mechanism. To 329implement these APIs, the toolchain provided builtin macro 'va_list', 'va_start' 330and 'va_end' cannot be avoided. This is because of some scenarios such as when 331'stack canaries' are enabled, only the compiler knows the format of the 'canary' 332in order to extract the parameters correctly. 333 334To provide a simple implementation, the following requirements are defined for 335'printf': 336 337- Format keyword 'xXduscp' needs to be supported. 338- Take '%' as escape flag, '%%' shows a '%' in the formatted string. 339- To save heap usage, 32 bytes buffer in the stack for collecting formatted 340 string. 341- Flush string outputting due to: a) buffer full b) function ends. 342 343The interface for flushing can be a logging device. 344 345Function needs implied inputs 346----------------------------- 347Take 'malloc' as an example. There is only one parameter for 'malloc' in 348the prototype. Heap management code is put in the SPRTL for sharing with caller 349partitions. The heap instance belongs to each partition, which means this 350instance needs to be passed into the heap management code as a parameter. For 351allocation API in heap management, it needs two parameters - 'size' and 352'instance', while for 'malloc' caller it needs a 'malloc' with one parameter 353'size' only. As mentioned in the upper chapter, this instance can be retrieved 354from the Secure Partition Local Storage. The implementation can be: 355 356.. code-block:: c 357 358 void *malloc(size_t sz) 359 { 360 struct p_sp_local_storage_t *m = 361 (struct p_sp_local_storage_t *)tfm_sprt_local_storage; 362 363 return tfm_sprt_alloc(m->heap_instance, sz); 364 } 365 366-------------- 367 368*Copyright (c) 2019-2024, Arm Limited. All rights reserved.* 369