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