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