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