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