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