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