1 /*
2  * Copyright (c) 2018-2023, 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_status_code.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.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(connection_pool, sizeof(struct connection_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  *  handle =      (p_connection - 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  *  p_connection    in RANGE[POOL_START, POOL_END]
88  *  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  */
connection_to_handle(struct connection_t * p_connection)95 psa_handle_t connection_to_handle(struct connection_t *p_connection)
96 {
97     psa_handle_t handle;
98 
99     loop_index = (loop_index + 1) % CONVERSION_FACTOR_VALUE;
100     handle = (psa_handle_t)((((uintptr_t)p_connection -
101                   (uintptr_t)connection_pool) << CONVERSION_FACTOR_BITOFFSET) +
102                   CLIENT_HANDLE_VALUE_MIN + loop_index);
103 
104     return 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  *  p_connection =    ((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  *  p_connection    in RANGE[POOL_START, POOL_END]
120  *  handle          in RANGE[CLIENT_HANDLE_VALUE_MIN, 0x3FFFFFFF]
121  *  loop_index      in RANGE[0, CONVERSION_FACTOR_VALUE - 1]
122  */
handle_to_connection(psa_handle_t handle)123 struct connection_t *handle_to_connection(psa_handle_t handle)
124 {
125     struct connection_t *p_connection;
126 
127     if (handle == PSA_NULL_HANDLE) {
128         return NULL;
129     }
130 
131     p_connection = (struct connection_t *)((((uintptr_t)handle -
132                     CLIENT_HANDLE_VALUE_MIN) >> CONVERSION_FACTOR_BITOFFSET) +
133                     (uintptr_t)connection_pool);
134 
135     return p_connection;
136 }
137 
138 /* Service handle management functions */
spm_allocate_connection(void)139 struct connection_t *spm_allocate_connection(void)
140 {
141     struct connection_t *p_handle;
142 
143     /* Get buffer for handle list structure from handle pool */
144     p_handle = (struct connection_t *)tfm_pool_alloc(connection_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 
spm_validate_connection(const struct connection_t * handle)156 psa_status_t spm_validate_connection(const struct connection_t *handle)
157 {
158     /* Check the handle address is valid */
159     if (is_valid_chunk_data_in_pool(connection_pool,
160                                     (uint8_t *)handle) != true) {
161         return SPM_ERROR_GENERIC;
162     }
163 
164     return PSA_SUCCESS;
165 }
166 
spm_free_connection(struct connection_t * p_connection)167 void spm_free_connection(struct connection_t *p_connection)
168 {
169     struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
170 
171     SPM_ASSERT(p_connection != NULL);
172 
173     CRITICAL_SECTION_ENTER(cs_assert);
174     /* Back handle buffer to pool */
175     tfm_pool_free(connection_pool, p_connection);
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 connection_t *spm_get_handle_by_signal(struct partition_t *p_ptn,
184                                               psa_signal_t signal)
185 {
186     struct connection_t *p_handle_iter;
187     struct connection_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_client_connection(psa_handle_t handle,int32_t client_id)313 struct connection_t *spm_get_client_connection(psa_handle_t handle,
314                                                int32_t client_id)
315 {
316     struct connection_t *p_conn_handle = handle_to_connection(handle);
317 
318     if (spm_validate_connection(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_msg_handle_to_connection(psa_handle_t msg_handle)331 struct connection_t *spm_msg_handle_to_connection(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 connection_t *p_conn_handle = handle_to_connection(msg_handle);
344 
345     if (spm_validate_connection(p_conn_handle) != PSA_SUCCESS) {
346         return NULL;
347     }
348 
349     /* Check that the running partition owns the message */
350     partition_id = tfm_spm_partition_get_running_partition_id();
351     if (partition_id != p_conn_handle->service->partition->p_ldinf->pid) {
352         return NULL;
353     }
354 
355     return p_conn_handle;
356 }
357 
spm_fill_message(struct connection_t * p_connection,struct service_t * service,psa_handle_t handle,int32_t type,int32_t client_id)358 void spm_fill_message(struct connection_t *p_connection,
359                       struct service_t *service,
360                       psa_handle_t handle,
361                       int32_t type, int32_t client_id)
362 {
363     SPM_ASSERT(p_connection);
364     SPM_ASSERT(service);
365 
366     /* Clear message buffer before using it */
367     spm_memset(&p_connection->msg, 0, sizeof(psa_msg_t));
368 
369     p_connection->service = service;
370     p_connection->p_client = GET_CURRENT_COMPONENT();
371     p_connection->msg.client_id = client_id;
372 
373     /* Copy contents */
374     p_connection->msg.type = type;
375 
376     /* Use the user connect handle as the message handle */
377     p_connection->msg.handle = handle;
378 
379 #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1
380     p_connection->msg.rhandle = p_connection->rhandle;
381 #endif
382 
383     /* Set the private data of NSPE client caller in multi-core topology */
384     if (TFM_CLIENT_ID_IS_NS(client_id)) {
385         tfm_rpc_set_caller_data(p_connection, client_id);
386     }
387 }
388 
tfm_spm_partition_get_running_partition_id(void)389 int32_t tfm_spm_partition_get_running_partition_id(void)
390 {
391     struct partition_t *partition;
392 
393     partition = GET_CURRENT_COMPONENT();
394     if (partition && partition->p_ldinf) {
395         return partition->p_ldinf->pid;
396     } else {
397         return INVALID_PARTITION_ID;
398     }
399 }
400 
tfm_spm_is_ns_caller(void)401 bool tfm_spm_is_ns_caller(void)
402 {
403     struct partition_t *partition = GET_CURRENT_COMPONENT();
404 
405     if (!partition) {
406         tfm_core_panic();
407     }
408 
409     return IS_NS_AGENT(partition->p_ldinf);
410 }
411 
tfm_spm_get_client_id(bool ns_caller)412 int32_t tfm_spm_get_client_id(bool ns_caller)
413 {
414     int32_t client_id;
415 
416     if (ns_caller) {
417         client_id = tfm_nspm_get_current_client_id();
418     } else {
419         client_id = tfm_spm_partition_get_running_partition_id();
420     }
421 
422     if (ns_caller != (client_id < 0)) {
423         /* NS client ID must be negative and Secure ID must >= 0 */
424         tfm_core_panic();
425     }
426 
427     return client_id;
428 }
429 
tfm_spm_init(void)430 uint32_t tfm_spm_init(void)
431 {
432     struct partition_t *partition;
433     uint32_t service_setting;
434     fih_int fih_rc = FIH_FAILURE;
435 
436     tfm_pool_init(connection_pool,
437                   POOL_BUFFER_SIZE(connection_pool),
438                   sizeof(struct connection_t),
439                   CONFIG_TFM_CONN_HANDLE_MAX_NUM);
440 
441     UNI_LISI_INIT_NODE(PARTITION_LIST_ADDR, next);
442     UNI_LISI_INIT_NODE(&services_listhead, next);
443 
444     /* Init the nonsecure context. */
445     tfm_nspm_ctx_init();
446 
447     while (1) {
448         partition = load_a_partition_assuredly(PARTITION_LIST_ADDR);
449         if (partition == NO_MORE_PARTITION) {
450             break;
451         }
452 
453         service_setting = load_services_assuredly(
454                                 partition,
455                                 &services_listhead,
456                                 stateless_services_ref_tbl,
457                                 sizeof(stateless_services_ref_tbl));
458 
459         load_irqs_assuredly(partition);
460 
461         /* Bind the partition with platform. */
462         FIH_CALL(tfm_hal_bind_boundary, fih_rc, partition->p_ldinf,
463                  &partition->boundary);
464         if (fih_not_eq(fih_rc, fih_int_encode(TFM_HAL_SUCCESS))) {
465             tfm_core_panic();
466         }
467 
468         backend_init_comp_assuredly(partition, service_setting);
469     }
470 
471     return backend_system_run();
472 }
473 
update_caller_outvec_len(struct connection_t * handle)474 void update_caller_outvec_len(struct connection_t *handle)
475 {
476     uint32_t i;
477 
478     for (i = 0; i < PSA_MAX_IOVEC; i++) {
479         if (handle->msg.out_size[i] == 0) {
480             continue;
481         }
482 
483         SPM_ASSERT(handle->caller_outvec[i].base == handle->outvec[i].base);
484 
485         handle->caller_outvec[i].len = handle->outvec[i].len;
486     }
487 }
488