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