1 /*
2 * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
3 * Copyright (c) 2022-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 <stdint.h>
12 #include "async.h"
13 #include "bitops.h"
14 #include "config_impl.h"
15 #include "config_spm.h"
16 #include "critical_section.h"
17 #include "internal_status_code.h"
18 #include "psa/lifecycle.h"
19 #include "psa/service.h"
20 #include "spm.h"
21 #include "tfm_arch.h"
22 #include "load/partition_defs.h"
23 #include "load/service_defs.h"
24 #include "load/interrupt_defs.h"
25 #include "utilities.h"
26 #include "ffm/backend.h"
27 #include "ffm/psa_api.h"
28 #include "tfm_hal_platform.h"
29 #include "tfm_plat_otp.h"
30 #include "tfm_psa_call_pack.h"
31 #include "tfm_hal_isolation.h"
32
spm_handle_programmer_errors(psa_status_t status)33 void spm_handle_programmer_errors(psa_status_t status)
34 {
35 if ((status == PSA_ERROR_PROGRAMMER_ERROR) ||
36 (status == PSA_ERROR_CONNECTION_REFUSED)) {
37 if (!tfm_spm_is_ns_caller()) {
38 tfm_core_panic();
39 }
40 }
41 }
42
tfm_spm_get_lifecycle_state(void)43 uint32_t tfm_spm_get_lifecycle_state(void)
44 {
45 enum tfm_plat_err_t err;
46 enum plat_otp_lcs_t otp_lcs;
47
48 err = tfm_plat_otp_read(PLAT_OTP_ID_LCS, sizeof(otp_lcs),
49 (uint8_t *)&otp_lcs);
50 if (err != TFM_PLAT_ERR_SUCCESS) {
51 return PSA_LIFECYCLE_UNKNOWN;
52 }
53
54 switch (otp_lcs) {
55 case PLAT_OTP_LCS_ASSEMBLY_AND_TEST:
56 return PSA_LIFECYCLE_ASSEMBLY_AND_TEST;
57 case PLAT_OTP_LCS_PSA_ROT_PROVISIONING:
58 return PSA_LIFECYCLE_PSA_ROT_PROVISIONING;
59 case PLAT_OTP_LCS_SECURED:
60 return PSA_LIFECYCLE_SECURED;
61 case PLAT_OTP_LCS_DECOMMISSIONED:
62 return PSA_LIFECYCLE_DECOMMISSIONED;
63 default:
64 return PSA_LIFECYCLE_UNKNOWN;
65 }
66 }
67
68 /* PSA Partition API function body */
69
70 #if (CONFIG_TFM_SPM_BACKEND_IPC == 1) \
71 || (CONFIG_TFM_FLIH_API == 1) || (CONFIG_TFM_SLIH_API == 1)
tfm_spm_partition_psa_wait(psa_signal_t signal_mask,uint32_t timeout)72 psa_signal_t tfm_spm_partition_psa_wait(psa_signal_t signal_mask,
73 uint32_t timeout)
74 {
75 struct partition_t *partition = NULL;
76 psa_signal_t signal;
77
78 /*
79 * Timeout[30:0] are reserved for future use.
80 * SPM must ignore the value of RES.
81 */
82 timeout &= PSA_TIMEOUT_MASK;
83
84 partition = GET_CURRENT_COMPONENT();
85
86 /*
87 * signals_allowed can be 0 for TF-M internal partitions for special usages.
88 * Regular Secure Partitions should have at least one signal.
89 * This is guaranteed by the manifest tool.
90 * It is a PROGRAMMER ERROR if the signal_mask does not include any assigned
91 * signals.
92 */
93 if ((partition->signals_allowed) &&
94 ((partition->signals_allowed & signal_mask) == 0)) {
95 tfm_core_panic();
96 }
97
98 /*
99 * After new signal(s) are available, the return value will be updated in
100 * PendSV and blocked thread gets to run.
101 */
102 if (timeout == PSA_BLOCK) {
103 signal = backend_wait_signals(partition, signal_mask);
104 if (signal == (psa_signal_t)0) {
105 signal = (psa_signal_t)STATUS_NEED_SCHEDULE;
106 }
107 } else {
108 signal = partition->signals_asserted & signal_mask;
109 }
110
111 return signal;
112 }
113 #endif
114
115 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
tfm_spm_partition_psa_get(psa_signal_t signal,psa_msg_t * msg)116 psa_status_t tfm_spm_partition_psa_get(psa_signal_t signal, psa_msg_t *msg)
117 {
118 psa_status_t ret = PSA_ERROR_GENERIC_ERROR;
119 struct connection_t *handle = NULL;
120 struct partition_t *partition = NULL;
121 fih_int fih_rc = FIH_FAILURE;
122
123 /*
124 * Only one message could be retrieved every time for psa_get(). It is a
125 * fatal error if the input signal has more than a signal bit set.
126 */
127 if (!IS_ONLY_ONE_BIT_IN_UINT32(signal)) {
128 tfm_core_panic();
129 }
130
131 partition = GET_CURRENT_COMPONENT();
132
133 /*
134 * Write the message to the service buffer. It is a fatal error if the
135 * input msg pointer is not a valid memory reference or not read-write.
136 */
137 FIH_CALL(tfm_hal_memory_check, fih_rc,
138 partition->boundary, (uintptr_t)msg,
139 sizeof(psa_msg_t), TFM_HAL_ACCESS_READWRITE);
140 if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
141 tfm_core_panic();
142 }
143
144 /*
145 * It is a fatal error if the caller call psa_get() when no message has
146 * been set. The caller must call this function after an RoT Service signal
147 * is returned by psa_wait().
148 */
149 if (partition->signals_asserted == 0) {
150 tfm_core_panic();
151 }
152
153 /*
154 * It is a fatal error if the RoT Service signal is not currently asserted.
155 */
156 if ((partition->signals_asserted & signal) == 0) {
157 tfm_core_panic();
158 }
159
160 if (signal == ASYNC_MSG_REPLY) {
161 handle = spm_get_async_replied_handle(partition);
162 msg->rhandle = handle;
163 ret = PSA_SUCCESS;
164 } else {
165 /*
166 * Get message by signal from partition. It is a fatal error if getting
167 * failed, which means the input signal does not correspond to an RoT service.
168 */
169 handle = spm_get_handle_by_signal(partition, signal);
170 if (handle) {
171 ret = PSA_SUCCESS;
172 } else {
173 return PSA_ERROR_DOES_NOT_EXIST;
174 }
175
176 spm_memcpy(msg, &handle->msg, sizeof(psa_msg_t));
177 }
178
179 return ret;
180 }
181 #endif
182
update_caller_outvec_len(struct connection_t * handle)183 static void update_caller_outvec_len(struct connection_t *handle)
184 {
185 uint32_t i;
186
187 #if PSA_FRAMEWORK_HAS_MM_IOVEC
188 /*
189 * If no unmapping call was made, output vectors will report that no data
190 * has been written.
191 */
192 for (i = OUTVEC_IDX_BASE; i < (PSA_MAX_IOVEC * 2); i++) {
193 if (!IOVEC_IS_UNMAPPED(handle, i) && !IOVEC_IS_ACCESSED(handle, i)) {
194 handle->outvec_written[i - OUTVEC_IDX_BASE] = 0;
195 }
196 }
197 #endif
198
199 /*
200 * The total number of bytes written to a single parameter must be reported
201 * to the client by updating the len member of the psa_outvec structure for
202 * the parameter before returning from psa_call().
203 */
204 for (i = 0; i < PSA_MAX_IOVEC; i++) {
205 if (handle->msg.out_size[i] == 0) {
206 continue;
207 }
208
209 assert(handle->caller_outvec[i].base == handle->outvec_base[i]);
210
211 handle->caller_outvec[i].len = handle->outvec_written[i];
212 }
213 }
214
psa_reply_error_connection(struct connection_t * handle,psa_status_t status,bool * del_conn)215 static inline psa_status_t psa_reply_error_connection(
216 struct connection_t *handle,
217 psa_status_t status,
218 bool *del_conn)
219 {
220 #if CONFIG_TFM_SPM_BACKEND_SFN == 1
221 *del_conn = true;
222 #else
223 (void)del_conn;
224 #endif
225
226 handle->status = TFM_HANDLE_STATUS_TO_FREE;
227
228 return status;
229 }
230
tfm_spm_partition_psa_reply(psa_handle_t msg_handle,psa_status_t status)231 psa_status_t tfm_spm_partition_psa_reply(psa_handle_t msg_handle,
232 psa_status_t status)
233 {
234 const struct service_t *service;
235 struct connection_t *handle;
236 psa_status_t ret = PSA_SUCCESS;
237 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
238 bool delete_connection = false;
239
240 /* It is a fatal error if message handle is invalid */
241 handle = spm_msg_handle_to_connection(msg_handle);
242 if (!handle) {
243 tfm_core_panic();
244 }
245
246 /*
247 * RoT Service information is needed in this function, stored it in message
248 * body structure. Only two parameters are passed in this function: handle
249 * and status, so it is useful and simply to do like this.
250 */
251 service = handle->service;
252 if (!service) {
253 tfm_core_panic();
254 }
255
256 switch (handle->msg.type) {
257 case PSA_IPC_CONNECT:
258 /*
259 * Reply to PSA_IPC_CONNECT message. Connect handle is returned if the
260 * input status is PSA_SUCCESS. Others return values are based on the
261 * input status.
262 */
263 if (status == PSA_SUCCESS) {
264 ret = msg_handle;
265 } else if (status == PSA_ERROR_CONNECTION_REFUSED) {
266 /* Refuse the client connection, indicating a permanent error. */
267 ret = psa_reply_error_connection(handle, status, &delete_connection);
268 } else if (status == PSA_ERROR_CONNECTION_BUSY) {
269 /* Fail the client connection, indicating a transient error. */
270 ret = psa_reply_error_connection(handle, status, &delete_connection);
271 } else {
272 tfm_core_panic();
273 }
274 break;
275 case PSA_IPC_DISCONNECT:
276 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
277 /* Service handle will be freed in the backend */
278 handle->status = TFM_HANDLE_STATUS_TO_FREE;
279 #else
280 /* Service handle is not used anymore */
281 delete_connection = true;
282 #endif
283
284 /*
285 * If the message type is PSA_IPC_DISCONNECT, then the status code is
286 * ignored
287 */
288 break;
289 default:
290 if (handle->msg.type >= PSA_IPC_CALL) {
291 /* Reply to a request message. Return values are based on status */
292 ret = status;
293
294 update_caller_outvec_len(handle);
295 if (SERVICE_IS_STATELESS(service->p_ldinf->flags)) {
296 handle->status = TFM_HANDLE_STATUS_TO_FREE;
297 #if CONFIG_TFM_SPM_BACKEND_SFN == 1
298 delete_connection = true;
299 #endif /* CONFIG_TFM_SPM_BACKEND_SFN == 1 */
300 }
301 } else {
302 tfm_core_panic();
303 }
304 }
305
306 if (ret == PSA_ERROR_PROGRAMMER_ERROR) {
307 /*
308 * If the source of the programmer error is a Secure Partition, the SPM
309 * must panic the Secure Partition in response to a PROGRAMMER ERROR.
310 */
311 if (!TFM_CLIENT_ID_IS_NS(handle->msg.client_id)) {
312 tfm_core_panic();
313 }
314 }
315
316 /*
317 * TODO: It can be optimized further by moving critical section protection
318 * to mailbox. Also need to check implementation when secure context is
319 * involved.
320 */
321 CRITICAL_SECTION_ENTER(cs_assert);
322 ret = backend_replying(handle, ret);
323 CRITICAL_SECTION_LEAVE(cs_assert);
324
325 /*
326 * When IPC model is using the asynchronous agent API, retain the handle
327 * until the response has been collected by the agent.
328 */
329 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
330 if (IS_NS_AGENT_MAILBOX(handle->p_client->p_ldinf)) {
331 return ret;
332 }
333 #endif
334
335 if (handle->status != TFM_HANDLE_STATUS_TO_FREE) {
336 handle->status = TFM_HANDLE_STATUS_IDLE;
337 }
338 /*
339 * When the asynchronous agent API is not used or when in SFN model, free
340 * the connection handle immediately.
341 */
342 if (delete_connection) {
343 handle->status = TFM_HANDLE_STATUS_IDLE;
344
345 spm_free_connection(handle);
346 }
347
348 return ret;
349 }
350
351 #if CONFIG_TFM_DOORBELL_API == 1
tfm_spm_partition_psa_notify(int32_t partition_id)352 psa_status_t tfm_spm_partition_psa_notify(int32_t partition_id)
353 {
354 struct partition_t *p_pt = tfm_spm_get_partition_by_id(partition_id);
355
356 return backend_assert_signal(p_pt, PSA_DOORBELL);
357 }
358
tfm_spm_partition_psa_clear(void)359 psa_status_t tfm_spm_partition_psa_clear(void)
360 {
361 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
362 struct partition_t *partition = NULL;
363
364 partition = GET_CURRENT_COMPONENT();
365
366 /*
367 * It is a fatal error if the Secure Partition's doorbell signal is not
368 * currently asserted.
369 */
370 if ((partition->signals_asserted & PSA_DOORBELL) == 0) {
371 tfm_core_panic();
372 }
373
374 CRITICAL_SECTION_ENTER(cs_assert);
375 partition->signals_asserted &= ~PSA_DOORBELL;
376 CRITICAL_SECTION_LEAVE(cs_assert);
377
378 return PSA_SUCCESS;
379 }
380 #endif /* CONFIG_TFM_DOORBELL_API == 1 */
381
tfm_spm_partition_psa_panic(void)382 psa_status_t tfm_spm_partition_psa_panic(void)
383 {
384 #ifdef CONFIG_TFM_HALT_ON_CORE_PANIC
385 tfm_hal_system_halt();
386 #else
387 /*
388 * PSA FF recommends that the SPM causes the system to restart when a secure
389 * partition panics.
390 */
391 tfm_hal_system_reset();
392 #endif
393
394 /* Suppress Pe111 (statement is unreachable) for IAR as return here is in
395 * case system reset fails, which should not happen */
396 #if defined(__ICCARM__)
397 #pragma diag_suppress = Pe111
398 #endif
399 /* Execution should not reach here */
400 return PSA_ERROR_GENERIC_ERROR;
401 #if defined(__ICCARM__)
402 #pragma diag_default = Pe111
403 #endif
404 }
405