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