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