1 /*
2 * Copyright (c) 2019-2023, Arm Limited. All rights reserved.
3 * Copyright (c) 2022-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 <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_rpc.h"
29 #include "tfm_hal_platform.h"
30 #include "tfm_psa_call_pack.h"
31 #include "tfm_hal_isolation.h"
32
33
34
spm_handle_programmer_errors(psa_status_t status)35 void spm_handle_programmer_errors(psa_status_t status)
36 {
37 if (status == PSA_ERROR_PROGRAMMER_ERROR ||
38 status == PSA_ERROR_CONNECTION_REFUSED) {
39 if (!tfm_spm_is_ns_caller()) {
40 tfm_core_panic();
41 }
42 }
43 }
44
tfm_spm_get_lifecycle_state(void)45 uint32_t tfm_spm_get_lifecycle_state(void)
46 {
47 /*
48 * FixMe: return PSA_LIFECYCLE_UNKNOWN to the caller directly. It will be
49 * implemented in the future.
50 */
51 return PSA_LIFECYCLE_UNKNOWN;
52 }
53
54 /* PSA Partition API function body */
55
56 #if CONFIG_TFM_SPM_BACKEND_IPC == 1 \
57 || CONFIG_TFM_FLIH_API == 1 || CONFIG_TFM_SLIH_API == 1
tfm_spm_partition_psa_wait(psa_signal_t signal_mask,uint32_t timeout)58 psa_signal_t tfm_spm_partition_psa_wait(psa_signal_t signal_mask,
59 uint32_t timeout)
60 {
61 struct partition_t *partition = NULL;
62 psa_signal_t signal;
63
64 /*
65 * Timeout[30:0] are reserved for future use.
66 * SPM must ignore the value of RES.
67 */
68 timeout &= PSA_TIMEOUT_MASK;
69
70 partition = GET_CURRENT_COMPONENT();
71
72 /*
73 * signals_allowed can be 0 for TF-M internal partitions for special usages.
74 * Regular Secure Partitions should have at least one signal.
75 * This is gauranteed by the manifest tool.
76 * It is a PROGRAMMER ERROR if the signal_mask does not include any assigned
77 * signals.
78 */
79 if ((partition->signals_allowed) &&
80 (partition->signals_allowed & signal_mask) == 0) {
81 tfm_core_panic();
82 }
83
84 /*
85 * After new signal(s) are available, the return value will be updated in
86 * PendSV and blocked thread gets to run.
87 */
88 if (timeout == PSA_BLOCK) {
89 signal = backend_wait_signals(partition, signal_mask);
90 if (signal == (psa_signal_t)0) {
91 signal = (psa_signal_t)STATUS_NEED_SCHEDULE;
92 }
93 } else {
94 signal = partition->signals_asserted & signal_mask;
95 }
96
97 return signal;
98 }
99 #endif
100
101 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
tfm_spm_partition_psa_get(psa_signal_t signal,psa_msg_t * msg)102 psa_status_t tfm_spm_partition_psa_get(psa_signal_t signal, psa_msg_t *msg)
103 {
104 psa_status_t ret = PSA_ERROR_GENERIC_ERROR;
105 struct connection_t *handle = NULL;
106 struct partition_t *partition = NULL;
107 fih_int fih_rc = FIH_FAILURE;
108
109 /*
110 * Only one message could be retrieved every time for psa_get(). It is a
111 * fatal error if the input signal has more than a signal bit set.
112 */
113 if (!IS_ONLY_ONE_BIT_IN_UINT32(signal)) {
114 tfm_core_panic();
115 }
116
117 partition = GET_CURRENT_COMPONENT();
118
119 /*
120 * Write the message to the service buffer. It is a fatal error if the
121 * input msg pointer is not a valid memory reference or not read-write.
122 */
123 FIH_CALL(tfm_hal_memory_check, fih_rc,
124 partition->boundary, (uintptr_t)msg,
125 sizeof(psa_msg_t), TFM_HAL_ACCESS_READWRITE);
126 if (fih_not_eq(fih_rc, fih_int_encode(PSA_SUCCESS))) {
127 tfm_core_panic();
128 }
129
130 /*
131 * It is a fatal error if the caller call psa_get() when no message has
132 * been set. The caller must call this function after an RoT Service signal
133 * is returned by psa_wait().
134 */
135 if (partition->signals_asserted == 0) {
136 tfm_core_panic();
137 }
138
139 /*
140 * It is a fatal error if the RoT Service signal is not currently asserted.
141 */
142 if ((partition->signals_asserted & signal) == 0) {
143 tfm_core_panic();
144 }
145
146 if (signal == ASYNC_MSG_REPLY) {
147 struct connection_t **pr_handle_iter, **prev = NULL;
148 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
149
150 /* Remove tail of the list, which is the first item added */
151 CRITICAL_SECTION_ENTER(cs_assert);
152 if (!partition->p_handles) {
153 tfm_core_panic();
154 }
155 UNI_LIST_FOREACH_NODE_PNODE(pr_handle_iter, handle,
156 partition, p_handles) {
157 prev = pr_handle_iter;
158 }
159 handle = *prev;
160 UNI_LIST_REMOVE_NODE_BY_PNODE(prev, p_handles);
161 ret = handle->reply_value;
162 /* Clear the signal if there are no more asynchronous responses waiting */
163 if (!partition->p_handles) {
164 partition->signals_asserted &= ~ASYNC_MSG_REPLY;
165 }
166 CRITICAL_SECTION_LEAVE(cs_assert);
167 } else {
168 /*
169 * Get message by signal from partition. It is a fatal error if getting
170 * failed, which means the input signal does not correspond to an RoT service.
171 */
172 handle = spm_get_handle_by_signal(partition, signal);
173 if (handle) {
174 ret = PSA_SUCCESS;
175 } else {
176 return PSA_ERROR_DOES_NOT_EXIST;
177 }
178 }
179
180 spm_memcpy(msg, &handle->msg, sizeof(psa_msg_t));
181
182 return ret;
183 }
184 #endif
185
tfm_spm_partition_psa_reply(psa_handle_t msg_handle,psa_status_t status)186 psa_status_t tfm_spm_partition_psa_reply(psa_handle_t msg_handle,
187 psa_status_t status)
188 {
189 struct service_t *service;
190 struct connection_t *handle;
191 psa_status_t ret = PSA_SUCCESS;
192 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
193
194 /* It is a fatal error if message handle is invalid */
195 handle = spm_msg_handle_to_connection(msg_handle);
196 if (!handle) {
197 tfm_core_panic();
198 }
199
200 /*
201 * RoT Service information is needed in this function, stored it in message
202 * body structure. Only two parameters are passed in this function: handle
203 * and status, so it is useful and simply to do like this.
204 */
205 service = handle->service;
206 if (!service) {
207 tfm_core_panic();
208 }
209
210 switch (handle->msg.type) {
211 case PSA_IPC_CONNECT:
212 /*
213 * Reply to PSA_IPC_CONNECT message. Connect handle is returned if the
214 * input status is PSA_SUCCESS. Others return values are based on the
215 * input status.
216 */
217 if (status == PSA_SUCCESS) {
218 ret = msg_handle;
219 } else if (status == PSA_ERROR_CONNECTION_REFUSED) {
220 /* Refuse the client connection, indicating a permanent error. */
221 ret = PSA_ERROR_CONNECTION_REFUSED;
222 handle->status = TFM_HANDLE_STATUS_TO_FREE;
223 } else if (status == PSA_ERROR_CONNECTION_BUSY) {
224 /* Fail the client connection, indicating a transient error. */
225 ret = PSA_ERROR_CONNECTION_BUSY;
226 } else {
227 tfm_core_panic();
228 }
229 break;
230 case PSA_IPC_DISCONNECT:
231 /* Service handle is not used anymore */
232 handle->status = TFM_HANDLE_STATUS_TO_FREE;
233
234 /*
235 * If the message type is PSA_IPC_DISCONNECT, then the status code is
236 * ignored
237 */
238 break;
239 default:
240 if (handle->msg.type >= PSA_IPC_CALL) {
241
242 #if PSA_FRAMEWORK_HAS_MM_IOVEC
243 /*
244 * Any output vectors that are still mapped will report that
245 * zero bytes have been written.
246 */
247 for (int i = OUTVEC_IDX_BASE; i < PSA_MAX_IOVEC * 2; i++) {
248 if (IOVEC_IS_MAPPED(handle, i) && (!IOVEC_IS_UNMAPPED(handle, i))) {
249 handle->outvec_written[i - OUTVEC_IDX_BASE] = 0;
250 }
251 }
252 #endif
253 /* Reply to a request message. Return values are based on status */
254 ret = status;
255 /*
256 * The total number of bytes written to a single parameter must be
257 * reported to the client by updating the len member of the
258 * psa_outvec structure for the parameter before returning from
259 * psa_call().
260 */
261 update_caller_outvec_len(handle);
262 if (SERVICE_IS_STATELESS(service->p_ldinf->flags)) {
263 handle->status = TFM_HANDLE_STATUS_TO_FREE;
264 }
265 } else {
266 tfm_core_panic();
267 }
268 }
269
270 if (ret == PSA_ERROR_PROGRAMMER_ERROR) {
271 /*
272 * If the source of the programmer error is a Secure Partition, the SPM
273 * must panic the Secure Partition in response to a PROGRAMMER ERROR.
274 */
275 if (!TFM_CLIENT_ID_IS_NS(handle->msg.client_id)) {
276 tfm_core_panic();
277 }
278 }
279
280 /*
281 * TODO: It can be optimized further by moving critical section protection
282 * to mailbox. Also need to check implementation when secure context is
283 * involved.
284 */
285 CRITICAL_SECTION_ENTER(cs_assert);
286 ret = backend_replying(handle, ret);
287 CRITICAL_SECTION_LEAVE(cs_assert);
288
289 /*
290 * When IPC model is using the asynchronous agent API, retain the handle
291 * until the response has been collected by the agent.
292 */
293 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
294 if (is_tfm_rpc_msg(handle)) {
295 return ret;
296 }
297 #endif
298
299 /*
300 * When the asynchronous agent API is not used or when in SFN model, free
301 * the connection handle immediately.
302 */
303 if (handle->status == TFM_HANDLE_STATUS_TO_FREE) {
304 spm_free_connection(handle);
305 } else {
306 handle->status = TFM_HANDLE_STATUS_IDLE;
307 }
308
309 return ret;
310 }
311
312 #if CONFIG_TFM_DOORBELL_API == 1
tfm_spm_partition_psa_notify(int32_t partition_id)313 psa_status_t tfm_spm_partition_psa_notify(int32_t partition_id)
314 {
315 struct partition_t *p_pt = tfm_spm_get_partition_by_id(partition_id);
316
317 return backend_assert_signal(p_pt, PSA_DOORBELL);
318 }
319
tfm_spm_partition_psa_clear(void)320 psa_status_t tfm_spm_partition_psa_clear(void)
321 {
322 struct critical_section_t cs_assert = CRITICAL_SECTION_STATIC_INIT;
323 struct partition_t *partition = NULL;
324
325 partition = GET_CURRENT_COMPONENT();
326
327 /*
328 * It is a fatal error if the Secure Partition's doorbell signal is not
329 * currently asserted.
330 */
331 if ((partition->signals_asserted & PSA_DOORBELL) == 0) {
332 tfm_core_panic();
333 }
334
335 CRITICAL_SECTION_ENTER(cs_assert);
336 partition->signals_asserted &= ~PSA_DOORBELL;
337 CRITICAL_SECTION_LEAVE(cs_assert);
338
339 return PSA_SUCCESS;
340 }
341 #endif /* CONFIG_TFM_DOORBELL_API == 1 */
342
tfm_spm_partition_psa_panic(void)343 psa_status_t tfm_spm_partition_psa_panic(void)
344 {
345 #ifdef CONFIG_TFM_HALT_ON_CORE_PANIC
346 tfm_hal_system_halt();
347 #else
348 /*
349 * PSA FF recommends that the SPM causes the system to restart when a secure
350 * partition panics.
351 */
352 tfm_hal_system_reset();
353 #endif
354
355 /* Execution should not reach here */
356 return PSA_ERROR_GENERIC_ERROR;
357 }
358