1 /*
2 * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
3 * Copyright (c) 2021-2024 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 <assert.h>
15 #include "async.h"
16 #include "bitops.h"
17 #include "config_impl.h"
18 #include "config_spm.h"
19 #include "critical_section.h"
20 #include "current.h"
21 #include "fih.h"
22 #include "psa/client.h"
23 #include "psa/service.h"
24 #include "thread.h"
25 #include "internal_status_code.h"
26 #include "tfm_arch.h"
27 #include "tfm_hal_defs.h"
28 #include "tfm_hal_interrupt.h"
29 #include "tfm_hal_isolation.h"
30 #include "spm.h"
31 #include "tfm_peripherals_def.h"
32 #include "tfm_nspm.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 /* Partition and service runtime data list head/runtime data table */
46 static struct service_head_t services_listhead;
47 struct service_t *stateless_services_ref_tbl[STATIC_HANDLE_NUM_LIMIT];
48
49 /* Partition management functions */
50
51 /* This API is only used in IPC backend. */
52 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
spm_get_async_replied_handle(struct partition_t * partition)53 struct connection_t *spm_get_async_replied_handle(struct partition_t *partition)
54 {
55 struct connection_t **pr_handle_iter, **prev = NULL, *handle = NULL;
56 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
57
58 /* Remove tail of the list, which is the first item added */
59 CRITICAL_SECTION_ENTER(cs_assert);
60 if (!partition->p_replied) {
61 tfm_core_panic();
62 }
63 UNI_LIST_FOREACH_NODE_PNODE(pr_handle_iter, handle,
64 partition, p_replied) {
65 prev = pr_handle_iter;
66 }
67 handle = *prev;
68 UNI_LIST_REMOVE_NODE_BY_PNODE(prev, p_replied);
69
70 /* Clear the signal if there are no more asynchronous responses waiting */
71 if (!partition->p_replied) {
72 partition->signals_asserted &= ~ASYNC_MSG_REPLY;
73 }
74 CRITICAL_SECTION_LEAVE(cs_assert);
75
76 return handle;
77 }
78
spm_get_handle_by_signal(struct partition_t * p_ptn,psa_signal_t signal)79 struct connection_t *spm_get_handle_by_signal(struct partition_t *p_ptn,
80 psa_signal_t signal)
81 {
82 struct connection_t *p_handle_iter;
83 struct connection_t **pr_handle_iter, **last_found_handle_holder = NULL;
84 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
85 uint32_t nr_found_msgs = 0;
86
87 CRITICAL_SECTION_ENTER(cs_assert);
88
89 /* Return the last found message which applies a FIFO mechanism. */
90 UNI_LIST_FOREACH_NODE_PNODE(pr_handle_iter, p_handle_iter,
91 p_ptn, p_reqs) {
92 if (p_handle_iter->service->p_ldinf->signal == signal) {
93 last_found_handle_holder = pr_handle_iter;
94 nr_found_msgs++;
95 }
96 }
97
98 if (last_found_handle_holder) {
99 p_handle_iter = *last_found_handle_holder;
100 UNI_LIST_REMOVE_NODE_BY_PNODE(last_found_handle_holder, p_reqs);
101
102 if (nr_found_msgs == 1) {
103 p_ptn->signals_asserted &= ~signal;
104 }
105 }
106
107 CRITICAL_SECTION_LEAVE(cs_assert);
108
109 return p_handle_iter;
110 }
111 #endif /* CONFIG_TFM_SPM_BACKEND_IPC == 1 */
112
tfm_spm_get_service_by_sid(uint32_t sid)113 const struct service_t *tfm_spm_get_service_by_sid(uint32_t sid)
114 {
115 struct service_t *p_prev, *p_curr;
116
117 UNI_LIST_FOREACH_NODE_PREV(p_prev, p_curr, &services_listhead, next) {
118 if (p_curr->p_ldinf->sid == sid) {
119 UNI_LIST_MOVE_AFTER(&services_listhead, p_prev, p_curr, next);
120 return p_curr;
121 }
122 }
123
124 return NULL;
125 }
126
127 #if CONFIG_TFM_DOORBELL_API == 1
128 /**
129 * \brief Get the partition context by partition ID.
130 *
131 * \param[in] partition_id Partition identity
132 *
133 * \retval NULL Failed
134 * \retval "Not NULL" Target partition context pointer,
135 * \ref partition_t structures
136 */
tfm_spm_get_partition_by_id(int32_t partition_id)137 struct partition_t *tfm_spm_get_partition_by_id(int32_t partition_id)
138 {
139 struct partition_t *p_part;
140
141 UNI_LIST_FOREACH(p_part, PARTITION_LIST_ADDR, next) {
142 if (p_part->p_ldinf->pid == partition_id) {
143 return p_part;
144 }
145 }
146
147 return NULL;
148 }
149 #endif /* CONFIG_TFM_DOORBELL_API == 1 */
150
tfm_spm_check_client_version(const struct service_t * service,uint32_t version)151 int32_t tfm_spm_check_client_version(const struct service_t *service,
152 uint32_t version)
153 {
154 assert(service);
155
156 switch (SERVICE_GET_VERSION_POLICY(service->p_ldinf->flags)) {
157 case SERVICE_VERSION_POLICY_RELAXED:
158 if (version > service->p_ldinf->version) {
159 return SPM_ERROR_VERSION;
160 }
161 break;
162 case SERVICE_VERSION_POLICY_STRICT:
163 if (version != service->p_ldinf->version) {
164 return SPM_ERROR_VERSION;
165 }
166 break;
167 default:
168 return SPM_ERROR_VERSION;
169 }
170 return PSA_SUCCESS;
171 }
172
tfm_spm_check_authorization(uint32_t sid,const struct service_t * service,bool ns_caller)173 int32_t tfm_spm_check_authorization(uint32_t sid,
174 const struct service_t *service,
175 bool ns_caller)
176 {
177 const struct partition_t *partition = NULL;
178 const uint32_t *dep;
179 int32_t i;
180
181 assert(service);
182
183 if (ns_caller) {
184 if (!SERVICE_IS_NS_ACCESSIBLE(service->p_ldinf->flags)) {
185 return SPM_ERROR_GENERIC;
186 }
187 } else {
188 partition = GET_CURRENT_COMPONENT();
189 if (!partition) {
190 tfm_core_panic();
191 }
192
193 dep = LOAD_INFO_DEPS(partition->p_ldinf);
194 for (i = 0; i < partition->p_ldinf->ndeps; i++) {
195 if (dep[i] == sid) {
196 break;
197 }
198 }
199
200 if (i == partition->p_ldinf->ndeps) {
201 return SPM_ERROR_GENERIC;
202 }
203 }
204 return PSA_SUCCESS;
205 }
206
207 /* Message functions */
spm_get_idle_connection(struct connection_t ** p_connection,psa_handle_t handle,int32_t client_id)208 psa_status_t spm_get_idle_connection(struct connection_t **p_connection,
209 psa_handle_t handle,
210 int32_t client_id)
211 {
212 struct connection_t *connection;
213 const struct service_t *service;
214 uint32_t sid, version, index;
215 int32_t psa_ret;
216 bool ns_caller;
217
218 assert(p_connection != NULL);
219
220 /* It is a PROGRAMMER ERROR if the handle is a null handle. */
221 if (handle == PSA_NULL_HANDLE) {
222 return PSA_ERROR_PROGRAMMER_ERROR;
223 }
224
225 if (IS_STATIC_HANDLE(handle)) {
226 /* Allocate space from handle pool for static handle. */
227 index = GET_INDEX_FROM_STATIC_HANDLE(handle);
228
229 service = stateless_services_ref_tbl[index];
230 if (service == NULL) {
231 return PSA_ERROR_PROGRAMMER_ERROR;
232 }
233
234 sid = service->p_ldinf->sid;
235 ns_caller = tfm_spm_is_ns_caller();
236
237 /*
238 * It is a PROGRAMMER ERROR if the caller is not authorized to access
239 * the RoT Service.
240 */
241 psa_ret = tfm_spm_check_authorization(sid, service, ns_caller);
242 if (psa_ret != PSA_SUCCESS) {
243 return PSA_ERROR_CONNECTION_REFUSED;
244 }
245
246 version = GET_VERSION_FROM_STATIC_HANDLE(handle);
247
248 if (tfm_spm_check_client_version(service, version) != PSA_SUCCESS) {
249 return PSA_ERROR_PROGRAMMER_ERROR;
250 }
251
252 /*
253 * Current SPM doesn't support multiple context management. There is
254 * only one instance in SPM to call the connection pool allocation.
255 * There is no need to be protected.
256 * Protection should be established after the context management is
257 * implemented.
258 */
259 connection = spm_allocate_connection();
260 if (connection == NULL) {
261 return PSA_ERROR_CONNECTION_BUSY;
262 }
263
264 spm_init_idle_connection(connection, service, client_id);
265 } else {
266 #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1
267 connection = handle_to_connection(handle);
268 if (connection == NULL) {
269 return PSA_ERROR_PROGRAMMER_ERROR;
270 }
271
272 if (spm_validate_connection(connection) != PSA_SUCCESS) {
273 return PSA_ERROR_PROGRAMMER_ERROR;
274 }
275
276 /* Validate the caller id in the connection handle equals client_id. */
277 if (connection->msg.client_id != client_id) {
278 return PSA_ERROR_PROGRAMMER_ERROR;
279 }
280
281 /*
282 * It is a PROGRAMMER ERROR if the connection is currently
283 * handling a request.
284 */
285 if (connection->status != TFM_HANDLE_STATUS_IDLE) {
286 return PSA_ERROR_PROGRAMMER_ERROR;
287 }
288
289 if (connection->service == NULL) {
290 /* FixMe: Need to implement a mechanism to resolve this failure. */
291 return PSA_ERROR_PROGRAMMER_ERROR;
292 }
293 #else
294 return PSA_ERROR_PROGRAMMER_ERROR;
295 #endif
296 }
297
298 *p_connection = connection;
299
300 return PSA_SUCCESS;
301 }
302
spm_msg_handle_to_connection(psa_handle_t msg_handle)303 struct connection_t *spm_msg_handle_to_connection(psa_handle_t msg_handle)
304 {
305 /*
306 * The message handler passed by the caller is considered invalid in the
307 * following cases:
308 * 1. Not a valid message handle. (The address of a message is not the
309 * address of a possible handle from the pool
310 * 2. Handle not belongs to the caller partition (The handle is either
311 * unused, or owned by another partition)
312 * Check the conditions above
313 */
314 int32_t partition_id;
315 struct connection_t *p_conn_handle = handle_to_connection(msg_handle);
316
317 if (spm_validate_connection(p_conn_handle) != PSA_SUCCESS) {
318 return NULL;
319 }
320
321 /* Check that the running partition owns the message */
322 partition_id = tfm_spm_partition_get_running_partition_id();
323 if (partition_id != p_conn_handle->service->partition->p_ldinf->pid) {
324 return NULL;
325 }
326
327 return p_conn_handle;
328 }
329
spm_init_idle_connection(struct connection_t * p_connection,const struct service_t * service,int32_t client_id)330 void spm_init_idle_connection(struct connection_t *p_connection,
331 const struct service_t *service,
332 int32_t client_id)
333 {
334 assert(p_connection);
335 assert(service);
336
337 /* Clear message buffer before using it */
338 spm_memset(&p_connection->msg, 0, sizeof(psa_msg_t));
339
340 p_connection->service = service;
341 p_connection->p_client = GET_CURRENT_COMPONENT();
342 p_connection->msg.client_id = client_id;
343 /* Use the user connect handle as the message handle */
344 p_connection->msg.handle = connection_to_handle(p_connection);
345
346 p_connection->status = TFM_HANDLE_STATUS_IDLE;
347 #if PSA_FRAMEWORK_HAS_MM_IOVEC
348 p_connection->iovec_status = 0;
349 #endif
350
351 #ifdef TFM_PARTITION_NS_AGENT_MAILBOX
352 p_connection->client_data = NULL;
353 #endif
354 }
355
tfm_spm_partition_get_running_partition_id(void)356 int32_t tfm_spm_partition_get_running_partition_id(void)
357 {
358 const struct partition_t *partition;
359
360 partition = GET_CURRENT_COMPONENT();
361 if (partition && partition->p_ldinf) {
362 return partition->p_ldinf->pid;
363 } else {
364 return INVALID_PARTITION_ID;
365 }
366 }
367
tfm_spm_is_ns_caller(void)368 bool tfm_spm_is_ns_caller(void)
369 {
370 const struct partition_t *partition = GET_CURRENT_COMPONENT();
371
372 if (!partition) {
373 tfm_core_panic();
374 }
375
376 return IS_NS_AGENT(partition->p_ldinf);
377 }
378
tfm_spm_get_client_id(bool ns_caller)379 int32_t tfm_spm_get_client_id(bool ns_caller)
380 {
381 int32_t client_id;
382
383 if (ns_caller) {
384 client_id = tfm_nspm_get_current_client_id();
385 } else {
386 client_id = tfm_spm_partition_get_running_partition_id();
387 }
388
389 if (ns_caller != (client_id < 0)) {
390 /* NS client ID must be negative and Secure ID must >= 0 */
391 tfm_core_panic();
392 }
393
394 return client_id;
395 }
396
tfm_spm_init(void)397 uint32_t tfm_spm_init(void)
398 {
399 struct partition_t *partition;
400 uint32_t service_setting;
401 fih_int fih_rc = FIH_FAILURE;
402
403 spm_init_connection_space();
404
405 UNI_LIST_INIT_NODE(PARTITION_LIST_ADDR, next);
406 UNI_LIST_INIT_NODE(&services_listhead, next);
407
408 /* Init the nonsecure context. */
409 tfm_nspm_ctx_init();
410
411 while (1) {
412 partition = load_a_partition_assuredly(PARTITION_LIST_ADDR);
413 if (partition == NO_MORE_PARTITION) {
414 break;
415 }
416
417 service_setting = load_services_assuredly(
418 partition,
419 &services_listhead,
420 stateless_services_ref_tbl,
421 sizeof(stateless_services_ref_tbl));
422
423 load_irqs_assuredly(partition);
424
425 /* Bind the partition with platform. */
426 FIH_CALL(tfm_hal_bind_boundary, fih_rc, partition->p_ldinf,
427 &partition->boundary);
428 if (fih_not_eq(fih_rc, fih_int_encode(TFM_HAL_SUCCESS))) {
429 tfm_core_panic();
430 }
431
432 backend_init_comp_assuredly(partition, service_setting);
433 }
434
435 #if CONFIG_TFM_POST_PARTITION_INIT_HOOK == 1
436 /*
437 * Platform can use CONFIG_TFM_POST_PARTITION_INIT_HOOK option to add extra initialization
438 * steps after static initialization of partitions' isolation has been completed.
439 */
440 FIH_CALL(tfm_hal_post_partition_init_hook, fih_rc);
441 if (fih_not_eq(fih_rc, fih_int_encode(TFM_HAL_SUCCESS))) {
442 tfm_core_panic();
443 }
444 #endif /* CONFIG_TFM_POST_PARTITION_INIT_HOOK == 1 */
445
446 return backend_system_run();
447 }
448