1 /*
2 * Copyright (c) 2019-2024, Arm Limited. All rights reserved.
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 gauranteed 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 struct connection_t **pr_handle_iter, **prev = NULL;
162 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
163
164 /* Remove tail of the list, which is the first item added */
165 CRITICAL_SECTION_ENTER(cs_assert);
166 if (!partition->p_handles) {
167 tfm_core_panic();
168 }
169 UNI_LIST_FOREACH_NODE_PNODE(pr_handle_iter, handle,
170 partition, p_handles) {
171 prev = pr_handle_iter;
172 }
173 handle = *prev;
174 UNI_LIST_REMOVE_NODE_BY_PNODE(prev, p_handles);
175 ret = handle->reply_value;
176 /* Clear the signal if there are no more asynchronous responses waiting */
177 if (!partition->p_handles) {
178 partition->signals_asserted &= ~ASYNC_MSG_REPLY;
179 }
180 CRITICAL_SECTION_LEAVE(cs_assert);
181 } else {
182 /*
183 * Get message by signal from partition. It is a fatal error if getting
184 * failed, which means the input signal does not correspond to an RoT service.
185 */
186 handle = spm_get_handle_by_signal(partition, signal);
187 if (handle) {
188 ret = PSA_SUCCESS;
189 } else {
190 return PSA_ERROR_DOES_NOT_EXIST;
191 }
192 }
193
194 spm_memcpy(msg, &handle->msg, sizeof(psa_msg_t));
195
196 return ret;
197 }
198 #endif
199
tfm_spm_partition_psa_reply(psa_handle_t msg_handle,psa_status_t status)200 psa_status_t tfm_spm_partition_psa_reply(psa_handle_t msg_handle,
201 psa_status_t status)
202 {
203 const struct service_t *service;
204 struct connection_t *handle;
205 psa_status_t ret = PSA_SUCCESS;
206 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
207
208 /* It is a fatal error if message handle is invalid */
209 handle = spm_msg_handle_to_connection(msg_handle);
210 if (!handle) {
211 tfm_core_panic();
212 }
213
214 /*
215 * RoT Service information is needed in this function, stored it in message
216 * body structure. Only two parameters are passed in this function: handle
217 * and status, so it is useful and simply to do like this.
218 */
219 service = handle->service;
220 if (!service) {
221 tfm_core_panic();
222 }
223
224 switch (handle->msg.type) {
225 case PSA_IPC_CONNECT:
226 /*
227 * Reply to PSA_IPC_CONNECT message. Connect handle is returned if the
228 * input status is PSA_SUCCESS. Others return values are based on the
229 * input status.
230 */
231 if (status == PSA_SUCCESS) {
232 ret = msg_handle;
233 } else if (status == PSA_ERROR_CONNECTION_REFUSED) {
234 /* Refuse the client connection, indicating a permanent error. */
235 ret = PSA_ERROR_CONNECTION_REFUSED;
236 handle->status = TFM_HANDLE_STATUS_TO_FREE;
237 } else if (status == PSA_ERROR_CONNECTION_BUSY) {
238 /* Fail the client connection, indicating a transient error. */
239 ret = PSA_ERROR_CONNECTION_BUSY;
240 } else {
241 tfm_core_panic();
242 }
243 break;
244 case PSA_IPC_DISCONNECT:
245 /* Service handle is not used anymore */
246 handle->status = TFM_HANDLE_STATUS_TO_FREE;
247
248 /*
249 * If the message type is PSA_IPC_DISCONNECT, then the status code is
250 * ignored
251 */
252 break;
253 default:
254 if (handle->msg.type >= PSA_IPC_CALL) {
255
256 #if PSA_FRAMEWORK_HAS_MM_IOVEC
257 /*
258 * Any output vectors that are still mapped will report that
259 * zero bytes have been written.
260 */
261 for (int i = OUTVEC_IDX_BASE; i < PSA_MAX_IOVEC * 2; i++) {
262 if (IOVEC_IS_MAPPED(handle, i) && (!IOVEC_IS_UNMAPPED(handle, i))) {
263 handle->outvec_written[i - OUTVEC_IDX_BASE] = 0;
264 }
265 }
266 #endif
267 /* Reply to a request message. Return values are based on status */
268 ret = status;
269 /*
270 * The total number of bytes written to a single parameter must be
271 * reported to the client by updating the len member of the
272 * psa_outvec structure for the parameter before returning from
273 * psa_call().
274 */
275 update_caller_outvec_len(handle);
276 if (SERVICE_IS_STATELESS(service->p_ldinf->flags)) {
277 handle->status = TFM_HANDLE_STATUS_TO_FREE;
278 }
279 } else {
280 tfm_core_panic();
281 }
282 }
283
284 if (ret == PSA_ERROR_PROGRAMMER_ERROR) {
285 /*
286 * If the source of the programmer error is a Secure Partition, the SPM
287 * must panic the Secure Partition in response to a PROGRAMMER ERROR.
288 */
289 if (!TFM_CLIENT_ID_IS_NS(handle->msg.client_id)) {
290 tfm_core_panic();
291 }
292 }
293
294 /*
295 * TODO: It can be optimized further by moving critical section protection
296 * to mailbox. Also need to check implementation when secure context is
297 * involved.
298 */
299 CRITICAL_SECTION_ENTER(cs_assert);
300 ret = backend_replying(handle, ret);
301 CRITICAL_SECTION_LEAVE(cs_assert);
302
303 /*
304 * When IPC model is using the asynchronous agent API, retain the handle
305 * until the response has been collected by the agent.
306 */
307 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
308 if (tfm_spm_is_rpc_msg(handle)) {
309 return ret;
310 }
311 #endif
312
313 /*
314 * When the asynchronous agent API is not used or when in SFN model, free
315 * the connection handle immediately.
316 */
317 if (handle->status == TFM_HANDLE_STATUS_TO_FREE) {
318 spm_free_connection(handle);
319 } else {
320 handle->status = TFM_HANDLE_STATUS_IDLE;
321 }
322
323 return ret;
324 }
325
326 #if CONFIG_TFM_DOORBELL_API == 1
tfm_spm_partition_psa_notify(int32_t partition_id)327 psa_status_t tfm_spm_partition_psa_notify(int32_t partition_id)
328 {
329 struct partition_t *p_pt = tfm_spm_get_partition_by_id(partition_id);
330
331 return backend_assert_signal(p_pt, PSA_DOORBELL);
332 }
333
tfm_spm_partition_psa_clear(void)334 psa_status_t tfm_spm_partition_psa_clear(void)
335 {
336 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
337 struct partition_t *partition = NULL;
338
339 partition = GET_CURRENT_COMPONENT();
340
341 /*
342 * It is a fatal error if the Secure Partition's doorbell signal is not
343 * currently asserted.
344 */
345 if ((partition->signals_asserted & PSA_DOORBELL) == 0) {
346 tfm_core_panic();
347 }
348
349 CRITICAL_SECTION_ENTER(cs_assert);
350 partition->signals_asserted &= ~PSA_DOORBELL;
351 CRITICAL_SECTION_LEAVE(cs_assert);
352
353 return PSA_SUCCESS;
354 }
355 #endif /* CONFIG_TFM_DOORBELL_API == 1 */
356
tfm_spm_partition_psa_panic(void)357 psa_status_t tfm_spm_partition_psa_panic(void)
358 {
359 #ifdef CONFIG_TFM_HALT_ON_CORE_PANIC
360 tfm_hal_system_halt();
361 #else
362 /*
363 * PSA FF recommends that the SPM causes the system to restart when a secure
364 * partition panics.
365 */
366 tfm_hal_system_reset();
367 #endif
368
369 /* Execution should not reach here */
370 return PSA_ERROR_GENERIC_ERROR;
371 }
372