1 /*
2 * Copyright (c) 2020-2024, Arm Limited. All rights reserved.
3 * Copyright (c) 2021-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 #ifndef __SPM_H__
12 #define __SPM_H__
13
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include "config_impl.h"
17 #include "config_spm.h"
18 #include "current.h"
19 #include "tfm_arch.h"
20 #include "lists.h"
21 #include "runtime_defs.h"
22 #include "thread.h"
23 #include "psa/service.h"
24 #include "load/partition_defs.h"
25 #include "load/interrupt_defs.h"
26
27 enum connection_status {
28 TFM_HANDLE_STATUS_IDLE = 0, /* Handle created, idle */
29 TFM_HANDLE_STATUS_ACTIVE = 1, /* Handle in use */
30 TFM_HANDLE_STATUS_TO_FREE = 2, /* Handle to be freed */
31 TFM_HANDLE_STATUS_MAX = 3,
32
33 _TFM_HANDLE_STATUS_PAD = UINT32_MAX,
34 };
35
36 /* The mask used for timeout values */
37 #define PSA_TIMEOUT_MASK PSA_BLOCK
38
39 /*
40 * Set a number limit for stateless handle.
41 * Valid handle must be positive, set client handle minimum value to 1.
42 */
43 #define STATIC_HANDLE_NUM_LIMIT 32
44 #define CLIENT_HANDLE_VALUE_MIN 1
45
46 /*
47 * Bit width can be increased to match STATIC_HANDLE_NUM_LIMIT,
48 * current allowed maximum bit width is 8 for 256 handles.
49 */
50 #define STATIC_HANDLE_IDX_BIT_WIDTH 5
51 #define STATIC_HANDLE_IDX_MASK \
52 (uint32_t)((1UL << STATIC_HANDLE_IDX_BIT_WIDTH) - 1)
53 #define GET_INDEX_FROM_STATIC_HANDLE(handle) \
54 (uint32_t)((handle) & STATIC_HANDLE_IDX_MASK)
55
56 #define STATIC_HANDLE_VER_BIT_WIDTH 8
57 #define STATIC_HANDLE_VER_OFFSET 8
58 #define STATIC_HANDLE_VER_MASK \
59 (uint32_t)((1UL << STATIC_HANDLE_VER_BIT_WIDTH) - 1)
60 #define GET_VERSION_FROM_STATIC_HANDLE(handle) \
61 (uint32_t)(((handle) >> STATIC_HANDLE_VER_OFFSET) & STATIC_HANDLE_VER_MASK)
62
63 /* Validate the static handle indicator bit */
64 #define STATIC_HANDLE_INDICATOR_OFFSET 30
65 #define IS_STATIC_HANDLE(handle) \
66 ((handle) & (1UL << STATIC_HANDLE_INDICATOR_OFFSET))
67
68 #define SPM_INVALID_PARTITION_IDX (~0U)
69
70 /* Get partition by thread or context data */
71 #define GET_THRD_OWNER(x) TO_CONTAINER(x, struct partition_t, thrd)
72 #define GET_CTX_OWNER(x) TO_CONTAINER(x, struct partition_t, ctx_ctrl)
73
74 /* Checks if the provided client ID is a non-secure client ID */
75 #define TFM_CLIENT_ID_IS_NS(client_id) ((client_id) < 0)
76
77 /* RoT connection handle list */
78 struct connection_t {
79 enum connection_status status;
80 struct partition_t *p_client; /* Caller partition */
81 const struct service_t *service; /* RoT service pointer */
82 psa_msg_t msg; /* PSA message body */
83 const void *invec_base[PSA_MAX_IOVEC]; /* Base addresses of invec from client */
84 size_t invec_accessed[PSA_MAX_IOVEC]; /* Size of data accessed by psa_read/skip */
85 void *outvec_base[PSA_MAX_IOVEC]; /* Base addresses of outvec from client */
86 size_t outvec_written[PSA_MAX_IOVEC]; /* Size of data written by psa_write */
87 psa_outvec *caller_outvec; /* Save caller outvec pointer for write length update*/
88 #ifdef TFM_PARTITION_NS_AGENT_MAILBOX
89 const void *client_data; /*
90 * Pointer to the private data of the
91 * client. It saves the mailbox private
92 * data in multi-core topology.
93 */
94 #endif
95 #if PSA_FRAMEWORK_HAS_MM_IOVEC
96 uint32_t iovec_status; /* MM-IOVEC status */
97 #endif
98 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
99 struct connection_t *p_reqs; /* Request handle(s) link */
100 struct connection_t *p_replied; /* Replied Handle(s) link */
101 uintptr_t replied_value; /* Result of this operation */
102 #endif
103 };
104
105 /* Partition runtime type */
106 struct partition_t {
107 const struct partition_load_info_t *p_ldinf;
108 uintptr_t boundary;
109 uint32_t signals_allowed;
110 uint32_t signals_waiting;
111 volatile uint32_t signals_asserted;
112 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
113 const struct runtime_metadata_t *p_metadata;
114 struct context_ctrl_t ctx_ctrl;
115 struct thread_t thrd; /* IPC model */
116 struct connection_t *p_replied; /* Handle(s) to record replied connections */
117 #else
118 uint32_t state; /* SFN model */
119 #endif
120 struct connection_t *p_reqs; /* Handle(s) to record request connections to service. */
121 struct partition_t *next;
122 };
123
124 /* RoT Service data */
125 struct service_t {
126 const struct service_load_info_t *p_ldinf; /* Service load info */
127 struct partition_t *partition; /* Owner of the service */
128 struct service_t *next; /* For list operation */
129 };
130
131 /**
132 * \brief Get the running partition ID.
133 *
134 * \return Returns the partition ID
135 */
136 int32_t tfm_spm_partition_get_running_partition_id(void);
137
138 /******************** Service handle management functions ********************/
139 void spm_init_connection_space(void);
140
141 struct connection_t *spm_allocate_connection(void);
142
143 psa_status_t spm_validate_connection(const struct connection_t *p_connection);
144
145 /* Panic if invalid connection is given. */
146 void spm_free_connection(struct connection_t *p_connection);
147
148 /******************** Partition management functions *************************/
149
150 #if CONFIG_TFM_SPM_BACKEND_IPC == 1
151
152 /*
153 * Get the replied handles in the asynchnorous reply mode. The first handle to
154 * be replied is at the tail of list. Take the handle one by one and clean the
155 * asynchronous signal after all handles are operated.
156 */
157 struct connection_t *spm_get_async_replied_handle(struct partition_t *partition);
158
159 /*
160 * Lookup and grab the last spotted handles containing the message
161 * by the given signal. Only ONE signal bit can be accepted in 'signal',
162 * multiple bits lead to 'no matched handles found to that signal'.
163 *
164 * Returns NULL if no handles matched with the given signal.
165 * Returns an internal handle instance if spotted, the instance
166 * is moved out of partition handles. Partition available signals
167 * also get updated based on the count of handles with given signal
168 * still in the partition handles.
169 */
170 struct connection_t *spm_get_handle_by_signal(struct partition_t *p_ptn,
171 psa_signal_t signal);
172 #endif /* CONFIG_TFM_SPM_BACKEND_IPC */
173
174 #if CONFIG_TFM_DOORBELL_API == 1
175 /**
176 * \brief Get partition by Partition ID.
177 *
178 * \param[in] partition_id The Partition ID of the partition to get
179 *
180 * \retval NULL Failed
181 * \retval "Not NULL" Return the partition context pointer
182 * \ref partition_t structures
183 */
184 struct partition_t *tfm_spm_get_partition_by_id(int32_t partition_id);
185 #endif /* CONFIG_TFM_DOORBELL_API == 1 */
186
187 /**
188 * \brief Get the service context by service ID.
189 *
190 * \param[in] sid RoT Service identity
191 *
192 * \retval NULL Failed
193 * \retval "Not NULL" Target service context pointer,
194 * \ref service_t structures
195 */
196 const struct service_t *tfm_spm_get_service_by_sid(uint32_t sid);
197
198 /************************ Message functions **********************************/
199
200 /**
201 * \brief Convert the given user handle to an SPM recognised
202 * connection and verify that it is a valid idle
203 * connection that the caller is authorised to access.
204 *
205 * \param[out] p_connection The address of connection pointer to be converted
206 * from the given user handle.
207 *
208 * \param[in] handle Either a static handle or a handle to an established
209 * connection that was returned by a prior psa_connect
210 * call.
211 *
212 * \param[in] client_id The client ID of the caller.
213 *
214 * \retval PSA_SUCCESS Success.
215 * \retval PSA_ERROR_CONNECTION_REFUSED The SPM or RoT Service has refused the
216 * connection.
217 * \retval PSA_ERROR_CONNECTION_BUSY The SPM or RoT Service cannot make the
218 * connection at the moment.
219 * \retval PSA_ERROR_PROGRAMMER_ERROR The handle is invalid, the caller is not
220 * authorised to use it or the connection is already
221 * handling a request.
222 */
223 psa_status_t spm_get_idle_connection(struct connection_t **p_connection,
224 psa_handle_t handle,
225 int32_t client_id);
226
227 /**
228 * \brief Convert the given message handle to SPM recognised
229 * handle and verify it.
230 *
231 * \param[in] msg_handle Message handle which is a reference generated
232 * by the SPM to a specific message.
233 *
234 * \return A SPM recognised handle or NULL. It is NULL when
235 * verification of the converted SPM handle fails.
236 * \ref connection_t structures
237 */
238 struct connection_t *spm_msg_handle_to_connection(psa_handle_t msg_handle);
239
240 /**
241 * \brief Initialize connection, fill in with the input
242 * information and set to idle.
243 *
244 * \param[in] p_connection The 'p_connection' to initialize and fill information in.
245 * \param[in] service Target service context pointer, which can be
246 * obtained by partition management functions
247 * \param[in] client_id Partition ID of the sender of the message
248 */
249 void spm_init_idle_connection(struct connection_t *p_connection,
250 const struct service_t *service,
251 int32_t client_id);
252
253 /*
254 * Update connection content with information extracted from control param,
255 * including message type and information of IO vectors if any.
256 */
257 psa_status_t spm_associate_call_params(struct connection_t *p_connection,
258 uint32_t ctrl_param,
259 const psa_invec *inptr,
260 psa_outvec *outptr);
261
262 /**
263 * \brief Check the client version according to
264 * version policy
265 *
266 * \param[in] service Target service context pointer, which can be get
267 * by partition management functions
268 * \param[in] version Client support version
269 *
270 * \retval PSA_SUCCESS Success
271 * \retval SPM_ERROR_BAD_PARAMETERS Bad parameters input
272 * \retval SPM_ERROR_VERSION Check failed
273 */
274 int32_t tfm_spm_check_client_version(const struct service_t *service,
275 uint32_t version);
276
277 /**
278 * \brief Check the client access authorization
279 *
280 * \param[in] sid Target RoT Service identity
281 * \param[in] service Target service context pointer, which can be get
282 * by partition management functions
283 * \param[in] ns_caller Whether from NS caller
284 *
285 * \retval PSA_SUCCESS Success
286 * \retval SPM_ERROR_GENERIC Authorization check failed
287 */
288 int32_t tfm_spm_check_authorization(uint32_t sid,
289 const struct service_t *service,
290 bool ns_caller);
291
292 /**
293 * \brief Get the ns_caller info from runtime context.
294 *
295 * \retval - true: the PSA API caller is from non-secure
296 * - false: the PSA API caller is from secure
297 */
298 bool tfm_spm_is_ns_caller(void);
299
300 /**
301 * \brief Get ID of current RoT Service client.
302 * This API ensures the caller gets a valid ID.
303 *
304 * \param[in] ns_caller If the client is Non-Secure or not.
305 *
306 * \retval The client ID
307 */
308 int32_t tfm_spm_get_client_id(bool ns_caller);
309
310 /*
311 * PendSV specified function.
312 *
313 * Parameters :
314 * exc_return - EXC_RETURN value for the PendSV handler
315 *
316 * Return:
317 * Pointers to context control (sp, splimit, dummy, lr) of the current and
318 * the next thread.
319 * Each takes 32 bits. The context control is used by PendSV_Handler to do
320 * context switch.
321 */
322 uint64_t ipc_schedule(uint32_t exc_return);
323
324 /**
325 * \brief SPM initialization implementation
326 *
327 * \details This function must be called under handler mode.
328 * \retval This function returns an EXC_RETURN value. Other
329 * faults would panic the execution and never
330 * returned.
331 */
332 uint32_t tfm_spm_init(void);
333
334 /**
335 * \brief Converts a handle instance into a corresponded user handle.
336 */
337 psa_handle_t connection_to_handle(struct connection_t *p_connection);
338
339 /**
340 * \brief Converts a user handle into a corresponded handle instance.
341 */
342 struct connection_t *handle_to_connection(psa_handle_t handle);
343
344
345 /* Following PSA APIs are only needed by connection-based services */
346 #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1
347
348 /* Handler for \ref psa_connect.
349 *
350 * \param[in] p_connection The address of connection pointer.
351 * \param[in] sid RoT Service identity.
352 * \param[in] version The version of the RoT Service.
353 * \param[in] client_id Client id of the service caller. It should be
354 * validated before this API is being called.
355 *
356 * \retval PSA_SUCCESS Success.
357 * \retval PSA_ERROR_CONNECTION_REFUSED The SPM or RoT Service has refused the
358 * connection.
359 * \retval PSA_ERROR_CONNECTION_BUSY The SPM or RoT Service cannot make the
360 * connection at the moment.
361 * \retval "Does not return" The RoT Service ID and version are not
362 * supported, or the caller is not permitted to
363 * access the service.
364 */
365 psa_status_t spm_psa_connect_client_id_associated(struct connection_t **p_connection,
366 uint32_t sid, uint32_t version,
367 int32_t client_id);
368
369 /* Handler for \ref psa_close.
370 *
371 * \param[in] handle Service handle to the connection to be closed,
372 * \ref psa_handle_t
373 * \param[in] client_id Client id of the connection caller.
374 *
375 * \retval PSA_SUCCESS Success.
376 * \retval PSA_ERROR_PROGRAMMER_ERROR The call is invalid, one or more of the
377 * following are true:
378 * \arg Called with a stateless handle.
379 * \arg An invalid handle was provided that is not
380 * the null handle.
381 * \arg The connection is handling a request.
382 */
383 psa_status_t spm_psa_close_client_id_associated(psa_handle_t handle, int32_t client_id);
384
385 #endif /* #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1 */
386
387 #ifdef TFM_PARTITION_NS_AGENT_MAILBOX
388
389 /*
390 * Check if the message was allocated for a non-secure request via RPC
391 *
392 * param[in] handle The connection handle context pointer
393 * connection_t structures
394 *
395 * retval true The message was allocated for a NS request via RPC.
396 * retval false Otherwise.
397 */
tfm_spm_is_rpc_msg(const struct connection_t * handle)398 __STATIC_INLINE bool tfm_spm_is_rpc_msg(const struct connection_t *handle)
399 {
400 if (handle && (handle->client_data) && (handle->msg.client_id < 0)) {
401 return true;
402 }
403
404 return false;
405 }
406 #else /* TFM_PARTITION_NS_AGENT_MAILBOX */
407
408 /* RPC is only available in multi-core scenario */
409 #define tfm_spm_is_rpc_msg(x) (false)
410
411 #endif /* TFM_PARTITION_NS_AGENT_MAILBOX */
412
413 #endif /* __SPM_H__ */
414