1 /*
2  * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
3  * Copyright (c) 2021-2022 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 #ifndef __SPM_IPC_H__
12 #define __SPM_IPC_H__
13 
14 #include <stdint.h>
15 #include "config_impl.h"
16 #include "config_spm.h"
17 #include "current.h"
18 #include "tfm_arch.h"
19 #include "lists.h"
20 #include "thread.h"
21 #include "psa/service.h"
22 #include "load/partition_defs.h"
23 #include "load/interrupt_defs.h"
24 
25 #define TFM_HANDLE_STATUS_IDLE          0 /* Handle created             */
26 #define TFM_HANDLE_STATUS_ACTIVE        1 /* Handle in use              */
27 #define TFM_HANDLE_STATUS_TO_FREE       2 /* Free the handle            */
28 
29 /* Privileged definitions for partition thread mode */
30 #define TFM_PARTITION_UNPRIVILEGED_MODE         (0U)
31 #define TFM_PARTITION_PRIVILEGED_MODE           (1U)
32 
33 #if TFM_LVL == 1
34 #define GET_PARTITION_PRIVILEGED_MODE(p_ldinf)     TFM_PARTITION_PRIVILEGED_MODE
35 #else
36 #define GET_PARTITION_PRIVILEGED_MODE(p_ldinf)  \
37             (IS_PARTITION_PSA_ROT(p_ldinf) ? TFM_PARTITION_PRIVILEGED_MODE : \
38                                              TFM_PARTITION_UNPRIVILEGED_MODE)
39 #endif
40 
41 /*
42  * Set a number limit for stateless handle.
43  * Valid handle must be positive, set client handle minimum value to 1.
44  */
45 #define STATIC_HANDLE_NUM_LIMIT         32
46 #define CLIENT_HANDLE_VALUE_MIN         1
47 
48 #define STATIC_HANDLE_IDX_BIT_WIDTH     8
49 #define STATIC_HANDLE_IDX_MASK \
50     (uint32_t)((1UL << STATIC_HANDLE_IDX_BIT_WIDTH) - 1)
51 #define GET_INDEX_FROM_STATIC_HANDLE(handle) \
52     (uint32_t)((handle) & STATIC_HANDLE_IDX_MASK)
53 
54 #define STATIC_HANDLE_VER_BIT_WIDTH     8
55 #define STATIC_HANDLE_VER_OFFSET        8
56 #define STATIC_HANDLE_VER_MASK \
57     (uint32_t)((1UL << STATIC_HANDLE_VER_BIT_WIDTH) - 1)
58 #define GET_VERSION_FROM_STATIC_HANDLE(handle) \
59     (uint32_t)(((handle) >> STATIC_HANDLE_VER_OFFSET) & STATIC_HANDLE_VER_MASK)
60 
61 /* Validate the static handle indicator bit */
62 #define STATIC_HANDLE_INDICATOR_OFFSET  30
63 #define IS_STATIC_HANDLE(handle) \
64     ((handle) & (1UL << STATIC_HANDLE_INDICATOR_OFFSET))
65 
66 /* Valid index should be [0, STATIC_HANDLE_NUM_LIMIT-1] */
67 #define IS_VALID_STATIC_HANDLE_IDX(index) \
68     ((uint32_t)(index) < STATIC_HANDLE_NUM_LIMIT)
69 
70 #define SPM_INVALID_PARTITION_IDX       (~0U)
71 
72 #define TFM_MSG_MAGIC_SFN               0x21216565
73 
74 /* Get partition by thread or context data */
75 #define GET_THRD_OWNER(x)        TO_CONTAINER(x, struct partition_t, thrd)
76 #define GET_CTX_OWNER(x)         TO_CONTAINER(x, struct partition_t, ctx_ctrl)
77 
78 /* RoT connection handle list */
79 struct conn_handle_t {
80     void *rhandle;                      /* Reverse handle value           */
81     uint32_t status;                    /*
82                                          * Status of handle, three valid
83                                          * options:
84                                          * TFM_HANDLE_STATUS_ACTIVE,
85                                          * TFM_HANDLE_STATUS_IDLE and
86                                          * TFM_HANDLE_STATUS_TO_FREE
87                                          */
88     struct partition_t *p_client;       /* Caller partition               */
89     struct service_t *service;          /* RoT service pointer            */
90     union {
91         struct sync_obj_t ack_evnt;     /* IPC - Ack response event       */
92         uint32_t sfn_magic;             /* SFN - Indicate a SFN message   */
93     };
94     psa_msg_t msg;                      /* PSA message body               */
95     psa_invec invec[PSA_MAX_IOVEC];     /* Put in/out vectors in msg body */
96     psa_outvec outvec[PSA_MAX_IOVEC];
97     psa_outvec *caller_outvec;          /*
98                                          * Save caller outvec pointer for
99                                          * write length update
100                                          */
101 #ifdef TFM_PARTITION_NS_AGENT_MAILBOX
102     const void *caller_data;            /*
103                                          * Pointer to the private data of the
104                                          * caller. It identifies the NSPE PSA
105                                          * client calls in multi-core topology
106                                          */
107 #endif
108 #if PSA_FRAMEWORK_HAS_MM_IOVEC
109     uint32_t iovec_status;              /* MM-IOVEC status                */
110 #endif
111     struct conn_handle_t *p_handles;    /* Handle(s) link                 */
112 };
113 
114 /* Partition runtime type */
115 struct partition_t {
116     const struct partition_load_info_t *p_ldinf;
117     void                               *p_interrupts;
118     void                               *p_metadata;
119     uintptr_t                          boundary;
120     uint32_t                           signals_allowed;
121     uint32_t                           signals_waiting;
122     volatile uint32_t                  signals_asserted;
123 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
124     struct context_ctrl_t              ctx_ctrl;
125     struct sync_obj_t                  waitobj;
126     struct thread_t                    thrd;            /* IPC model */
127 #else
128     uint32_t                           state;           /* SFN model */
129 #endif
130     struct conn_handle_t               *p_handles;
131     struct partition_t                 *next;
132 };
133 
134 /* RoT Service data */
135 struct service_t {
136     const struct service_load_info_t *p_ldinf;     /* Service load info      */
137     struct partition_t *partition;                 /* Owner of the service   */
138     struct service_t *next;                        /* For list operation     */
139 };
140 
141 /**
142  * \brief   Get the running partition ID.
143  *
144  * \return  Returns the partition ID
145  */
146 int32_t tfm_spm_partition_get_running_partition_id(void);
147 
148 /******************** Service handle management functions ********************/
149 
150 /**
151  * \brief                   Create connection handle for client connect
152  *
153  * \retval NULL             Create failed
154  * \retval "Not NULL"       Service handle created
155  */
156 struct conn_handle_t *tfm_spm_create_conn_handle(void);
157 
158 /**
159  * \brief                   Validate connection handle for client connect
160  *
161  * \param[in] conn_handle   Handle to be validated
162  *
163  * \retval PSA_SUCCESS        Success
164  * \retval SPM_ERROR_GENERIC  Invalid handle
165  */
166 psa_status_t tfm_spm_validate_conn_handle(const struct conn_handle_t *conn_handle);
167 
168 /**
169  * \brief                   Free connection handle which not used anymore.
170  *
171  * \param[in] conn_handle   Connection handle created by
172  *                          tfm_spm_create_conn_handle()
173  *
174  * \retval "Does not return"  Panic for not find service by handle
175  */
176 void tfm_spm_free_conn_handle(struct conn_handle_t *conn_handle);
177 
178 /******************** Partition management functions *************************/
179 
180 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
181 /*
182  * Lookup and grab the last spotted handles containing the message
183  * by the given signal. Only ONE signal bit can be accepted in 'signal',
184  * multiple bits lead to 'no matched handles found to that signal'.
185  *
186  * Returns NULL if no handles matched with the given signal.
187  * Returns an internal handle instance if spotted, the instance
188  * is moved out of partition handles. Partition available signals
189  * also get updated based on the count of handles with given signal
190  * still in the partition handles.
191  */
192 struct conn_handle_t *spm_get_handle_by_signal(struct partition_t *p_ptn,
193                                                psa_signal_t signal);
194 #endif /* CONFIG_TFM_SPM_BACKEND_IPC */
195 
196 #if CONFIG_TFM_DOORBELL_API == 1
197 /**
198  * \brief                   Get partition by Partition ID.
199  *
200  * \param[in] partition_id  The Partition ID of the partition to get
201  *
202  * \retval NULL             Failed
203  * \retval "Not NULL"       Return the parttion context pointer
204  *                          \ref partition_t structures
205  */
206 struct partition_t *tfm_spm_get_partition_by_id(int32_t partition_id);
207 #endif /* CONFIG_TFM_DOORBELL_API == 1 */
208 
209 /**
210  * \brief                   Get the service context by service ID.
211  *
212  * \param[in] sid           RoT Service identity
213  *
214  * \retval NULL             Failed
215  * \retval "Not NULL"       Target service context pointer,
216  *                          \ref service_t structures
217  */
218 struct service_t *tfm_spm_get_service_by_sid(uint32_t sid);
219 
220 /************************ Message functions **********************************/
221 
222 #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1
223 /**
224  * \brief                   Convert the given client handle to SPM recognised
225  *                          handle and verify it.
226  *
227  * \param[in] handle        A handle to an established connection that is
228  *                          returned by a prior psa_connect call.
229  *
230  * \return                  A SPM recognised handle or NULL. It is NULL when
231  *                          verification of the converted SPM handle fails.
232  *                          \ref conn_handle_t structures
233  */
234 struct conn_handle_t *spm_get_handle_by_client_handle(psa_handle_t handle,
235                                                       int32_t client_id);
236 #endif
237 
238 /**
239  * \brief                   Convert the given message handle to SPM recognised
240  *                          handle and verify it.
241  *
242  * \param[in] msg_handle    Message handle which is a reference generated
243  *                          by the SPM to a specific message.
244  *
245  * \return                  A SPM recognised handle or NULL. It is NULL when
246  *                          verification of the converted SPM handle fails.
247  *                          \ref conn_handle_t structures
248  */
249 struct conn_handle_t *spm_get_handle_by_msg_handle(psa_handle_t msg_handle);
250 
251 /**
252  * \brief                   Fill the user message in handle.
253  *
254  * \param[in] conn_handle   The 'conn_handle' contains the user message.
255  * \param[in] service       Target service context pointer, which can be
256  *                          obtained by partition management functions
257  * \prarm[in] handle        Connect handle return by psa_connect().
258  * \param[in] type          Message type, PSA_IPC_CONNECT, PSA_IPC_CALL or
259  *                          PSA_IPC_DISCONNECT
260  * \param[in] client_id     Partition ID of the sender of the message
261  * \param[in] invec         Array of input \ref psa_invec structures
262  * \param[in] in_len        Number of input \ref psa_invec structures
263  * \param[in] outvec        Array of output \ref psa_outvec structures
264  * \param[in] out_len       Number of output \ref psa_outvec structures
265  * \param[in] caller_outvec Array of caller output \ref psa_outvec structures
266  */
267 void spm_fill_message(struct conn_handle_t *conn_handle,
268                       struct service_t *service,
269                       psa_handle_t handle,
270                       int32_t type, int32_t client_id,
271                       psa_invec *invec, size_t in_len,
272                       psa_outvec *outvec, size_t out_len,
273                       psa_outvec *caller_outvec);
274 
275 /**
276  * \brief                   Check the client version according to
277  *                          version policy
278  *
279  * \param[in] service       Target service context pointer, which can be get
280  *                          by partition management functions
281  * \param[in] version       Client support version
282  *
283  * \retval PSA_SUCCESS      Success
284  * \retval SPM_ERROR_BAD_PARAMETERS Bad parameters input
285  * \retval SPM_ERROR_VERSION Check failed
286  */
287 int32_t tfm_spm_check_client_version(struct service_t *service,
288                                      uint32_t version);
289 
290 /**
291  * \brief                   Check the client access authorization
292  *
293  * \param[in] sid           Target RoT Service identity
294  * \param[in] service       Target service context pointer, which can be get
295  *                          by partition management functions
296  * \param[in] ns_caller     Whether from NS caller
297  *
298  * \retval PSA_SUCCESS      Success
299  * \retval SPM_ERROR_GENERIC Authorization check failed
300  */
301 int32_t tfm_spm_check_authorization(uint32_t sid,
302                                     struct service_t *service,
303                                     bool ns_caller);
304 
305 /**
306  * \brief                       Get the ns_caller info from runtime context.
307  *
308  * \retval                      - true: the PSA API caller is from non-secure
309  *                              - false: the PSA API caller is from secure
310  */
311 bool tfm_spm_is_ns_caller(void);
312 
313 /**
314  * \brief               Get ID of current RoT Service client.
315  *                      This API ensures the caller gets a valid ID.
316  *
317  * \param[in] ns_caller If the client is Non-Secure or not.
318  *
319  * \retval              The client ID
320  */
321 int32_t tfm_spm_get_client_id(bool ns_caller);
322 
323 /*
324  * PendSV specified function.
325  *
326  * Parameters :
327  *  p_actx        -    Architecture context storage pointer
328  *
329  * Return:
330  *  Pointers to context control (sp, splimit, dummy, lr) of the current and
331  *  the next thread.
332  *  Each takes 32 bits. The context control is used by PendSV_Handler to do
333  *  context switch.
334  */
335 uint64_t ipc_schedule(void);
336 
337 /**
338  * \brief                      SPM initialization implementation
339  *
340  * \details                    This function must be called under handler mode.
341  * \retval                     This function returns an EXC_RETURN value. Other
342  *                             faults would panic the execution and never
343  *                             returned.
344  */
345 uint32_t tfm_spm_init(void);
346 
347 /**
348  * \brief Converts a handle instance into a corresponded user handle.
349  */
350 psa_handle_t tfm_spm_to_user_handle(struct conn_handle_t *handle_instance);
351 
352 /**
353  * \brief Converts a user handle into a corresponded handle instance.
354  */
355 struct conn_handle_t *tfm_spm_to_handle_instance(psa_handle_t user_handle);
356 
357 /**
358  * \brief Move to handler mode by a SVC for specific purpose
359  */
360 void tfm_core_handler_mode(void);
361 
362 void update_caller_outvec_len(struct conn_handle_t *handle);
363 
364 /*
365  * Set partition signal.
366  *
367  * Assert a signal to given partition.
368  */
369 void spm_assert_signal(void *p_pt, psa_signal_t signal);
370 
371 #if CONFIG_TFM_PSA_API_CROSS_CALL == 1
372 
373 /*
374  * SPM dispatcher to handle the API call under non-privileged model.
375  * This API runs under callers stack, and switch to SPM stack when
376  * calling 'p_fn', then switch back to caller stack before returning
377  * to the caller.
378  *
379  * fn_addr      - the target function to be called.
380  * frame_addr   - Address of the customized ABI frame. The frame must be
381  *                stored in the caller's stack (which means the frame variable
382  *                must be a local variable).
383  */
384 void spm_interface_cross_dispatcher(uintptr_t fn_addr, uintptr_t frame_addr);
385 
386 /* Execute a customized ABI function in C */
387 psa_status_t cross_call_entering_c(uintptr_t fn_addr, uintptr_t frame_addr);
388 
389 /* Execute a customized ABI function in C */
390 void cross_call_exiting_c(psa_status_t status, uintptr_t frame_addr);
391 
392 #endif
393 
394 #endif /* __SPM_IPC_H__ */
395