1 /*
2  * Copyright (c) 2018-2022, Arm Limited. All rights reserved.
3  * Copyright (c) 2021-2022 Cypress Semiconductor Corporation (an Infineon
4  * company) or an affiliate of Cypress Semiconductor Corporation. All rights
5  * reserved.
6  *
7  * SPDX-License-Identifier: BSD-3-Clause
8  *
9  */
10 
11 #include <inttypes.h>
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include "bitops.h"
15 #include "config_impl.h"
16 #include "config_spm.h"
17 #include "critical_section.h"
18 #include "current.h"
19 #include "fih.h"
20 #include "psa/client.h"
21 #include "psa/service.h"
22 #include "thread.h"
23 #include "internal_errors.h"
24 #include "tfm_api.h"
25 #include "tfm_arch.h"
26 #include "tfm_hal_defs.h"
27 #include "tfm_hal_interrupt.h"
28 #include "tfm_hal_isolation.h"
29 #include "spm_ipc.h"
30 #include "tfm_peripherals_def.h"
31 #include "tfm_nspm.h"
32 #include "tfm_rpc.h"
33 #include "tfm_core_trustzone.h"
34 #include "lists.h"
35 #include "tfm_pools.h"
36 #include "region.h"
37 #include "psa_manifest/pid.h"
38 #include "ffm/backend.h"
39 #include "load/partition_defs.h"
40 #include "load/service_defs.h"
41 #include "load/asset_defs.h"
42 #include "load/spm_load_api.h"
43 #include "tfm_nspm.h"
44 
45 #if !(defined CONFIG_TFM_CONN_HANDLE_MAX_NUM) || (CONFIG_TFM_CONN_HANDLE_MAX_NUM == 0)
46 #error "CONFIG_TFM_CONN_HANDLE_MAX_NUM must be defined and not zero."
47 #endif
48 
49 /* Partition and service runtime data list head/runtime data table */
50 static struct service_head_t services_listhead;
51 struct service_t *stateless_services_ref_tbl[STATIC_HANDLE_NUM_LIMIT];
52 
53 /* Pools */
54 TFM_POOL_DECLARE(conn_handle_pool, sizeof(struct conn_handle_t),
55                  CONFIG_TFM_CONN_HANDLE_MAX_NUM);
56 
57 /*********************** Connection handle conversion APIs *******************/
58 
59 #define CONVERSION_FACTOR_BITOFFSET    3
60 #define CONVERSION_FACTOR_VALUE        (1 << CONVERSION_FACTOR_BITOFFSET)
61 /* Set 32 as the maximum */
62 #define CONVERSION_FACTOR_VALUE_MAX    0x20
63 
64 #if CONVERSION_FACTOR_VALUE > CONVERSION_FACTOR_VALUE_MAX
65 #error "CONVERSION FACTOR OUT OF RANGE"
66 #endif
67 
68 static uint32_t loop_index;
69 
70 /*
71  * A handle instance psa_handle_t allocated inside SPM is actually a memory
72  * address among the handle pool. Return this handle to the client directly
73  * exposes information of secure memory address. In this case, converting the
74  * handle into another value does not represent the memory address to avoid
75  * exposing secure memory directly to clients.
76  *
77  * This function converts the handle instance into another value by scaling the
78  * handle in pool offset, the converted value is named as a user handle.
79  *
80  * The formula:
81  *  user_handle = (handle_instance - POOL_START) * CONVERSION_FACTOR_VALUE +
82  *                CLIENT_HANDLE_VALUE_MIN + loop_index
83  * where:
84  *  CONVERSION_FACTOR_VALUE = 1 << CONVERSION_FACTOR_BITOFFSET, and should not
85  *  exceed CONVERSION_FACTOR_VALUE_MAX.
86  *
87  *  handle_instance in RANGE[POOL_START, POOL_END]
88  *  user_handle     in RANGE[CLIENT_HANDLE_VALUE_MIN, 0x3FFFFFFF]
89  *  loop_index      in RANGE[0, CONVERSION_FACTOR_VALUE - 1]
90  *
91  *  note:
92  *  loop_index is used to promise same handle instance is converted into
93  *  different user handles in short time.
94  */
tfm_spm_to_user_handle(struct conn_handle_t * handle_instance)95 psa_handle_t tfm_spm_to_user_handle(struct conn_handle_t *handle_instance)
96 {
97     psa_handle_t user_handle;
98 
99     loop_index = (loop_index + 1) % CONVERSION_FACTOR_VALUE;
100     user_handle = (psa_handle_t)((((uintptr_t)handle_instance -
101                   (uintptr_t)conn_handle_pool) << CONVERSION_FACTOR_BITOFFSET) +
102                   CLIENT_HANDLE_VALUE_MIN + loop_index);
103 
104     return user_handle;
105 }
106 
107 /*
108  * This function converts a user handle into a corresponded handle instance.
109  * The converted value is validated before returning, an invalid handle instance
110  * is returned as NULL.
111  *
112  * The formula:
113  *  handle_instance = ((user_handle - CLIENT_HANDLE_VALUE_MIN) /
114  *                    CONVERSION_FACTOR_VALUE) + POOL_START
115  * where:
116  *  CONVERSION_FACTOR_VALUE = 1 << CONVERSION_FACTOR_BITOFFSET, and should not
117  *  exceed CONVERSION_FACTOR_VALUE_MAX.
118  *
119  *  handle_instance in RANGE[POOL_START, POOL_END]
120  *  user_handle     in RANGE[CLIENT_HANDLE_VALUE_MIN, 0x3FFFFFFF]
121  *  loop_index      in RANGE[0, CONVERSION_FACTOR_VALUE - 1]
122  */
tfm_spm_to_handle_instance(psa_handle_t user_handle)123 struct conn_handle_t *tfm_spm_to_handle_instance(psa_handle_t user_handle)
124 {
125     struct conn_handle_t *handle_instance;
126 
127     if (user_handle == PSA_NULL_HANDLE) {
128         return NULL;
129     }
130 
131     handle_instance = (struct conn_handle_t *)((((uintptr_t)user_handle -
132                       CLIENT_HANDLE_VALUE_MIN) >> CONVERSION_FACTOR_BITOFFSET) +
133                       (uintptr_t)conn_handle_pool);
134 
135     return handle_instance;
136 }
137 
138 /* Service handle management functions */
tfm_spm_create_conn_handle(void)139 struct conn_handle_t *tfm_spm_create_conn_handle(void)
140 {
141     struct conn_handle_t *p_handle;
142 
143     /* Get buffer for handle list structure from handle pool */
144     p_handle = (struct conn_handle_t *)tfm_pool_alloc(conn_handle_pool);
145     if (!p_handle) {
146         return NULL;
147     }
148 
149     spm_memset(p_handle, 0, sizeof(*p_handle));
150 
151     p_handle->status = TFM_HANDLE_STATUS_IDLE;
152 
153     return p_handle;
154 }
155 
tfm_spm_validate_conn_handle(const struct conn_handle_t * handle)156 psa_status_t tfm_spm_validate_conn_handle(const struct conn_handle_t *handle)
157 {
158     /* Check the handle address is valid */
159     if (is_valid_chunk_data_in_pool(conn_handle_pool,
160                                     (uint8_t *)handle) != true) {
161         return SPM_ERROR_GENERIC;
162     }
163 
164     return PSA_SUCCESS;
165 }
166 
tfm_spm_free_conn_handle(struct conn_handle_t * conn_handle)167 void tfm_spm_free_conn_handle(struct conn_handle_t *conn_handle)
168 {
169     struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
170 
171     SPM_ASSERT(conn_handle != NULL);
172 
173     CRITICAL_SECTION_ENTER(cs_assert);
174     /* Back handle buffer to pool */
175     tfm_pool_free(conn_handle_pool, conn_handle);
176     CRITICAL_SECTION_LEAVE(cs_assert);
177 }
178 
179 /* Partition management functions */
180 
181 /* This API is only used in IPC backend. */
182 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
spm_get_handle_by_signal(struct partition_t * p_ptn,psa_signal_t signal)183 struct conn_handle_t *spm_get_handle_by_signal(struct partition_t *p_ptn,
184                                                psa_signal_t signal)
185 {
186     struct conn_handle_t *p_handle_iter;
187     struct conn_handle_t **pr_handle_iter, **last_found_handle_holder = NULL;
188     struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
189     uint32_t nr_found_msgs = 0;
190 
191     CRITICAL_SECTION_ENTER(cs_assert);
192 
193     /* Return the last found message which applies a FIFO mechanism. */
194     UNI_LIST_FOREACH_NODE_PNODE(pr_handle_iter, p_handle_iter,
195                                 p_ptn, p_handles) {
196         if (p_handle_iter->service->p_ldinf->signal == signal) {
197             last_found_handle_holder = pr_handle_iter;
198             nr_found_msgs++;
199         }
200     }
201 
202     if (last_found_handle_holder) {
203         p_handle_iter = *last_found_handle_holder;
204         UNI_LIST_REMOVE_NODE_BY_PNODE(last_found_handle_holder, p_handles);
205 
206         if (nr_found_msgs == 1) {
207             p_ptn->signals_asserted &= ~signal;
208         }
209     }
210 
211     CRITICAL_SECTION_LEAVE(cs_assert);
212 
213     return p_handle_iter;
214 }
215 #endif /* CONFIG_TFM_SPM_BACKEND_IPC == 1 */
216 
tfm_spm_get_service_by_sid(uint32_t sid)217 struct service_t *tfm_spm_get_service_by_sid(uint32_t sid)
218 {
219     struct service_t *p_prev, *p_curr;
220 
221     UNI_LIST_FOREACH_NODE_PREV(p_prev, p_curr, &services_listhead, next) {
222         if (p_curr->p_ldinf->sid == sid) {
223             UNI_LIST_MOVE_AFTER(&services_listhead, p_prev, p_curr, next);
224             return p_curr;
225         }
226     }
227 
228     return NULL;
229 }
230 
231 #if CONFIG_TFM_DOORBELL_API == 1
232 /**
233  * \brief                   Get the partition context by partition ID.
234  *
235  * \param[in] partition_id  Partition identity
236  *
237  * \retval NULL             Failed
238  * \retval "Not NULL"       Target partition context pointer,
239  *                          \ref partition_t structures
240  */
tfm_spm_get_partition_by_id(int32_t partition_id)241 struct partition_t *tfm_spm_get_partition_by_id(int32_t partition_id)
242 {
243     struct partition_t *p_part;
244 
245     UNI_LIST_FOREACH(p_part, PARTITION_LIST_ADDR, next) {
246         if (p_part->p_ldinf->pid == partition_id) {
247             return p_part;
248         }
249     }
250 
251     return NULL;
252 }
253 #endif /* CONFIG_TFM_DOORBELL_API == 1 */
254 
tfm_spm_check_client_version(struct service_t * service,uint32_t version)255 int32_t tfm_spm_check_client_version(struct service_t *service,
256                                      uint32_t version)
257 {
258     SPM_ASSERT(service);
259 
260     switch (SERVICE_GET_VERSION_POLICY(service->p_ldinf->flags)) {
261     case SERVICE_VERSION_POLICY_RELAXED:
262         if (version > service->p_ldinf->version) {
263             return SPM_ERROR_VERSION;
264         }
265         break;
266     case SERVICE_VERSION_POLICY_STRICT:
267         if (version != service->p_ldinf->version) {
268             return SPM_ERROR_VERSION;
269         }
270         break;
271     default:
272         return SPM_ERROR_VERSION;
273     }
274     return PSA_SUCCESS;
275 }
276 
tfm_spm_check_authorization(uint32_t sid,struct service_t * service,bool ns_caller)277 int32_t tfm_spm_check_authorization(uint32_t sid,
278                                     struct service_t *service,
279                                     bool ns_caller)
280 {
281     struct partition_t *partition = NULL;
282     const uint32_t *dep;
283     int32_t i;
284 
285     SPM_ASSERT(service);
286 
287     if (ns_caller) {
288         if (!SERVICE_IS_NS_ACCESSIBLE(service->p_ldinf->flags)) {
289             return SPM_ERROR_GENERIC;
290         }
291     } else {
292         partition = GET_CURRENT_COMPONENT();
293         if (!partition) {
294             tfm_core_panic();
295         }
296 
297         dep = LOAD_INFO_DEPS(partition->p_ldinf);
298         for (i = 0; i < partition->p_ldinf->ndeps; i++) {
299             if (dep[i] == sid) {
300                 break;
301             }
302         }
303 
304         if (i == partition->p_ldinf->ndeps) {
305             return SPM_ERROR_GENERIC;
306         }
307     }
308     return PSA_SUCCESS;
309 }
310 
311 /* Message functions */
312 #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1
spm_get_handle_by_client_handle(psa_handle_t handle,int32_t client_id)313 struct conn_handle_t *spm_get_handle_by_client_handle(psa_handle_t handle,
314                                                       int32_t client_id)
315 {
316     struct conn_handle_t *p_conn_handle = tfm_spm_to_handle_instance(handle);
317 
318     if (tfm_spm_validate_conn_handle(p_conn_handle) != PSA_SUCCESS) {
319         return NULL;
320     }
321 
322     /* Validate the caller id in the connection handle equals client_id. */
323     if (p_conn_handle->msg.client_id != client_id) {
324         return NULL;
325     }
326 
327     return p_conn_handle;
328 }
329 #endif
330 
spm_get_handle_by_msg_handle(psa_handle_t msg_handle)331 struct conn_handle_t *spm_get_handle_by_msg_handle(psa_handle_t msg_handle)
332 {
333     /*
334      * The message handler passed by the caller is considered invalid in the
335      * following cases:
336      *   1. Not a valid message handle. (The address of a message is not the
337      *      address of a possible handle from the pool
338      *   2. Handle not belongs to the caller partition (The handle is either
339      *      unused, or owned by another partition)
340      * Check the conditions above
341      */
342     int32_t partition_id;
343     struct conn_handle_t *p_conn_handle =
344                                     tfm_spm_to_handle_instance(msg_handle);
345 
346     if (tfm_spm_validate_conn_handle(p_conn_handle) != PSA_SUCCESS) {
347         return NULL;
348     }
349 
350     /* Check that the running partition owns the message */
351     partition_id = tfm_spm_partition_get_running_partition_id();
352     if (partition_id != p_conn_handle->service->partition->p_ldinf->pid) {
353         return NULL;
354     }
355 
356     return p_conn_handle;
357 }
358 
spm_fill_message(struct conn_handle_t * conn_handle,struct service_t * service,psa_handle_t handle,int32_t type,int32_t client_id,psa_invec * invec,size_t in_len,psa_outvec * outvec,size_t out_len,psa_outvec * caller_outvec)359 void spm_fill_message(struct conn_handle_t *conn_handle,
360                       struct service_t *service,
361                       psa_handle_t handle,
362                       int32_t type, int32_t client_id,
363                       psa_invec *invec, size_t in_len,
364                       psa_outvec *outvec, size_t out_len,
365                       psa_outvec *caller_outvec)
366 {
367     uint32_t i;
368 
369     SPM_ASSERT(conn_handle);
370     SPM_ASSERT(service);
371     SPM_ASSERT(!(invec == NULL && in_len != 0));
372     SPM_ASSERT(!(outvec == NULL && out_len != 0));
373     SPM_ASSERT(in_len <= SIZE_MAX - out_len);
374     SPM_ASSERT(in_len + out_len <= PSA_MAX_IOVEC);
375 
376     /* Clear message buffer before using it */
377     spm_memset(&conn_handle->msg, 0, sizeof(psa_msg_t));
378 
379     THRD_SYNC_INIT(&conn_handle->ack_evnt);
380     conn_handle->service = service;
381     conn_handle->p_client = GET_CURRENT_COMPONENT();
382     conn_handle->caller_outvec = caller_outvec;
383     conn_handle->msg.client_id = client_id;
384 
385     /* Copy contents */
386     conn_handle->msg.type = type;
387 
388     for (i = 0; i < in_len; i++) {
389         conn_handle->msg.in_size[i] = invec[i].len;
390         conn_handle->invec[i].base = invec[i].base;
391     }
392 
393     for (i = 0; i < out_len; i++) {
394         conn_handle->msg.out_size[i] = outvec[i].len;
395         conn_handle->outvec[i].base = outvec[i].base;
396         /* Out len is used to record the wrote number, set 0 here again */
397         conn_handle->outvec[i].len = 0;
398     }
399 
400     /* Use the user connect handle as the message handle */
401     conn_handle->msg.handle = handle;
402     conn_handle->msg.rhandle = conn_handle->rhandle;
403 
404     /* Set the private data of NSPE client caller in multi-core topology */
405     if (TFM_CLIENT_ID_IS_NS(client_id)) {
406         tfm_rpc_set_caller_data(conn_handle, client_id);
407     }
408 }
409 
tfm_spm_partition_get_running_partition_id(void)410 int32_t tfm_spm_partition_get_running_partition_id(void)
411 {
412     struct partition_t *partition;
413 
414     partition = GET_CURRENT_COMPONENT();
415     if (partition && partition->p_ldinf) {
416         return partition->p_ldinf->pid;
417     } else {
418         return INVALID_PARTITION_ID;
419     }
420 }
421 
tfm_spm_is_ns_caller(void)422 bool tfm_spm_is_ns_caller(void)
423 {
424     struct partition_t *partition = GET_CURRENT_COMPONENT();
425 
426     if (!partition) {
427         tfm_core_panic();
428     }
429 
430     return IS_PARTITION_NS_AGENT(partition->p_ldinf);
431 }
432 
tfm_spm_get_client_id(bool ns_caller)433 int32_t tfm_spm_get_client_id(bool ns_caller)
434 {
435     int32_t client_id;
436 
437     if (ns_caller) {
438         client_id = tfm_nspm_get_current_client_id();
439     } else {
440         client_id = tfm_spm_partition_get_running_partition_id();
441     }
442 
443     if (ns_caller != (client_id < 0)) {
444         /* NS client ID must be negative and Secure ID must >= 0 */
445         tfm_core_panic();
446     }
447 
448     return client_id;
449 }
450 
tfm_spm_init(void)451 uint32_t tfm_spm_init(void)
452 {
453     struct partition_t *partition;
454     uint32_t service_setting;
455     fih_int fih_rc = FIH_FAILURE;
456 
457     tfm_pool_init(conn_handle_pool,
458                   POOL_BUFFER_SIZE(conn_handle_pool),
459                   sizeof(struct conn_handle_t),
460                   CONFIG_TFM_CONN_HANDLE_MAX_NUM);
461 
462     UNI_LISI_INIT_NODE(PARTITION_LIST_ADDR, next);
463     UNI_LISI_INIT_NODE(&services_listhead, next);
464 
465     /* Init the nonsecure context. */
466     tfm_nspm_ctx_init();
467 
468     while (1) {
469         partition = load_a_partition_assuredly(PARTITION_LIST_ADDR);
470         if (partition == NO_MORE_PARTITION) {
471             break;
472         }
473 
474         service_setting = load_services_assuredly(
475                                 partition,
476                                 &services_listhead,
477                                 stateless_services_ref_tbl,
478                                 sizeof(stateless_services_ref_tbl));
479 
480         load_irqs_assuredly(partition);
481 
482         /* Bind the partition with platform. */
483         FIH_CALL(tfm_hal_bind_boundary, fih_rc, partition->p_ldinf,
484                  &partition->boundary);
485         if (fih_not_eq(fih_rc, fih_int_encode(TFM_HAL_SUCCESS))) {
486             tfm_core_panic();
487         }
488 
489         backend_init_comp_assuredly(partition, service_setting);
490     }
491 
492     return backend_system_run();
493 }
494 
update_caller_outvec_len(struct conn_handle_t * handle)495 void update_caller_outvec_len(struct conn_handle_t *handle)
496 {
497     uint32_t i;
498 
499     /*
500      * FixeMe: abstract these part into dedicated functions to avoid
501      * accessing thread context in psa layer
502      */
503     /*
504      * If it is a NS request via RPC, the owner of this message is not set.
505      * Or if it is a SFN message, it does not have owner thread state either.
506      */
507     if ((!is_tfm_rpc_msg(handle)) && (handle->sfn_magic != TFM_MSG_MAGIC_SFN)) {
508         SPM_ASSERT(handle->ack_evnt.owner->state == THRD_STATE_BLOCK);
509     }
510 
511     for (i = 0; i < PSA_MAX_IOVEC; i++) {
512         if (handle->msg.out_size[i] == 0) {
513             continue;
514         }
515 
516         SPM_ASSERT(handle->caller_outvec[i].base == handle->outvec[i].base);
517 
518         handle->caller_outvec[i].len = handle->outvec[i].len;
519     }
520 }
521 
spm_assert_signal(void * p_pt,psa_signal_t signal)522 void spm_assert_signal(void *p_pt, psa_signal_t signal)
523 {
524     struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
525     struct partition_t *partition = (struct partition_t *)p_pt;
526 
527     if (!partition) {
528         tfm_core_panic();
529     }
530 
531     CRITICAL_SECTION_ENTER(cs_assert);
532 
533     partition->signals_asserted |= signal;
534 
535     if (partition->signals_waiting & signal) {
536         backend_wake_up(partition);
537     }
538 
539     CRITICAL_SECTION_LEAVE(cs_assert);
540 }
541