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