1 /*
2  * Copyright (c) 2018-2024, Arm Limited. All rights reserved.
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 "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_core_trustzone.h"
32 #include "lists.h"
33 #include "tfm_pools.h"
34 #include "region.h"
35 #include "psa_manifest/pid.h"
36 #include "ffm/backend.h"
37 #include "load/partition_defs.h"
38 #include "load/service_defs.h"
39 #include "load/asset_defs.h"
40 #include "load/spm_load_api.h"
41 #include "tfm_nspm.h"
42 #include "private/assert.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 const 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(const struct service_t * service,uint32_t version)124 int32_t tfm_spm_check_client_version(const 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,const struct service_t * service,bool ns_caller)146 int32_t tfm_spm_check_authorization(uint32_t sid,
147                                     const 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_idle_connection(struct connection_t ** p_connection,psa_handle_t handle,int32_t client_id)181 psa_status_t spm_get_idle_connection(struct connection_t **p_connection,
182                                      psa_handle_t handle,
183                                      int32_t client_id)
184 {
185     struct connection_t *connection;
186     const 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_idle_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_idle_connection(struct connection_t * p_connection,const struct service_t * service,int32_t client_id)296 void spm_init_idle_connection(struct connection_t *p_connection,
297                               const 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     p_connection->client_data = NULL;
319 #endif
320 }
321 
tfm_spm_partition_get_running_partition_id(void)322 int32_t tfm_spm_partition_get_running_partition_id(void)
323 {
324     struct partition_t *partition;
325 
326     partition = GET_CURRENT_COMPONENT();
327     if (partition && partition->p_ldinf) {
328         return partition->p_ldinf->pid;
329     } else {
330         return INVALID_PARTITION_ID;
331     }
332 }
333 
tfm_spm_is_ns_caller(void)334 bool tfm_spm_is_ns_caller(void)
335 {
336     struct partition_t *partition = GET_CURRENT_COMPONENT();
337 
338     if (!partition) {
339         tfm_core_panic();
340     }
341 
342     return IS_NS_AGENT(partition->p_ldinf);
343 }
344 
tfm_spm_get_client_id(bool ns_caller)345 int32_t tfm_spm_get_client_id(bool ns_caller)
346 {
347     int32_t client_id;
348 
349     if (ns_caller) {
350         client_id = tfm_nspm_get_current_client_id();
351     } else {
352         client_id = tfm_spm_partition_get_running_partition_id();
353     }
354 
355     if (ns_caller != (client_id < 0)) {
356         /* NS client ID must be negative and Secure ID must >= 0 */
357         tfm_core_panic();
358     }
359 
360     return client_id;
361 }
362 
tfm_spm_init(void)363 uint32_t tfm_spm_init(void)
364 {
365     struct partition_t *partition;
366     uint32_t service_setting;
367     fih_int fih_rc = FIH_FAILURE;
368 
369     spm_init_connection_space();
370 
371     UNI_LISI_INIT_NODE(PARTITION_LIST_ADDR, next);
372     UNI_LISI_INIT_NODE(&services_listhead, next);
373 
374     /* Init the nonsecure context. */
375     tfm_nspm_ctx_init();
376 
377     while (1) {
378         partition = load_a_partition_assuredly(PARTITION_LIST_ADDR);
379         if (partition == NO_MORE_PARTITION) {
380             break;
381         }
382 
383         service_setting = load_services_assuredly(
384                                 partition,
385                                 &services_listhead,
386                                 stateless_services_ref_tbl,
387                                 sizeof(stateless_services_ref_tbl));
388 
389         load_irqs_assuredly(partition);
390 
391         /* Bind the partition with platform. */
392         FIH_CALL(tfm_hal_bind_boundary, fih_rc, partition->p_ldinf,
393                  &partition->boundary);
394         if (fih_not_eq(fih_rc, fih_int_encode(TFM_HAL_SUCCESS))) {
395             tfm_core_panic();
396         }
397 
398         backend_init_comp_assuredly(partition, service_setting);
399     }
400 
401     return backend_system_run();
402 }
403 
update_caller_outvec_len(struct connection_t * handle)404 void update_caller_outvec_len(struct connection_t *handle)
405 {
406     uint32_t i;
407 
408     for (i = 0; i < PSA_MAX_IOVEC; i++) {
409         if (handle->msg.out_size[i] == 0) {
410             continue;
411         }
412 
413         SPM_ASSERT(handle->caller_outvec[i].base == handle->outvec_base[i]);
414 
415         handle->caller_outvec[i].len = handle->outvec_written[i];
416     }
417 }
418