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