1 /*
2  * Copyright (c) 2019-2023, Arm Limited. All rights reserved.
3  * Copyright (c) 2024 Cypress Semiconductor Corporation (an Infineon company)
4  * or an affiliate of Cypress Semiconductor Corporation. All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  *
8  */
9 
10 /* Data types and API definitions in NSPE mailbox library */
11 
12 #ifndef __TFM_NS_MAILBOX_H__
13 #define __TFM_NS_MAILBOX_H__
14 
15 #include <stdbool.h>
16 #include <stdint.h>
17 
18 #include "tfm_mailbox.h"
19 
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23 
24 #if !defined(TFM_MULTI_CORE_NS_OS) && (NUM_MAILBOX_QUEUE_SLOT > 1)
25 #pragma message("Note: NUM_MAILBOX_QUEUE_SLOT is set to more than 1 in NS bare metal environment")
26 #endif
27 
28 /* If there's no dcache at all, the SCB cache functions won't exist */
29 /* If the mailbox is uncached on the NS side, no need to flush and invalidate */
30 #if !defined(__DCACHE_PRESENT) || (__DCACHE_PRESENT == 0U) || (MAILBOX_IS_UNCACHED_NS == 1)
31 #define MAILBOX_CLEAN_CACHE(addr, size) __DSB()
32 #define MAILBOX_INVALIDATE_CACHE(addr, size) do {} while (0)
33 #else
34 #define MAILBOX_CLEAN_CACHE(addr, size) SCB_CleanDCache_by_Addr((addr), (size))
35 #define MAILBOX_INVALIDATE_CACHE(addr, size) SCB_InvalidateDCache_by_Addr((addr), (size))
36 #endif
37 
38 /*
39  * A single slot structure in NSPE mailbox queue for NS side only.
40  * This information is needed to handle mailbox requests/responses on NS side.
41  * It may contain information about thread that initiated request and is waiting on response.
42  */
43 struct ns_mailbox_slot_t {
44     const void *owner;                      /* Handle of owner task. */
45     int32_t    *reply;                      /* Address of reply value belonging
46                                              * to owner task.
47                                              */
48 #ifdef TFM_MULTI_CORE_NS_OS_MAILBOX_THREAD
49     uint8_t    *woken_flag;                 /* Indicate that owner task has been
50                                              * or should be woken up, after the
51                                              * reply is received.
52                                              */
53 #else
54     bool        is_woken;                   /* Indicate that owner task has been
55                                              * or should be woken up, after the
56                                              * reply is received.
57                                              */
58 #endif
59 };
60 
61 /* NSPE mailbox queue */
62 struct ns_mailbox_queue_t {
63     struct mailbox_status_t status MAILBOX_ALIGN;
64     struct mailbox_slot_t slots[NUM_MAILBOX_QUEUE_SLOT];
65 
66     /* Following data are not shared with secure */
67     struct ns_mailbox_slot_t slots_ns[NUM_MAILBOX_QUEUE_SLOT] MAILBOX_ALIGN;
68 
69     mailbox_queue_status_t   empty_slots;       /* Bitmask of empty slots */
70 
71 #ifdef TFM_MULTI_CORE_TEST
72     uint32_t                 nr_tx;             /* The total number of
73                                                  * submission of NS PSA Client
74                                                  * calls from NS task via
75                                                  * mailbox.
76                                                  */
77     uint32_t                 nr_used_slots;     /* The total number of used
78                                                  * mailbox queue slots each time
79                                                  * NS thread requests a mailbox
80                                                  * queue slot.
81                                                  */
82 #endif
83 
84     bool                     is_full;           /* Queue if full */
85 };
86 
87 /**
88  * \brief NSPE mailbox initialization
89  *
90  * \param[in] queue             The base address of NSPE mailbox queue to be
91  *                              initialized.
92  *
93  * \retval MAILBOX_SUCCESS      Operation succeeded.
94  * \retval Other return code    Operation failed with an error code.
95  */
96 int32_t tfm_ns_mailbox_init(struct ns_mailbox_queue_t *queue);
97 
98 /**
99  * \brief Send PSA client call to SPE via mailbox. Wait and fetch PSA client
100  *        call result.
101  *
102  * \param[in] call_type         PSA client call type
103  * \param[in] params            Parameters used for PSA client call
104  * \param[in] client_id         Optional client ID of non-secure caller.
105  *                              It is required to identify the non-secure caller
106  *                              when NSPE OS enforces non-secure task isolation.
107  * \param[out] reply            The buffer written with PSA client call result.
108  *
109  * \retval MAILBOX_SUCCESS      The PSA client call is completed successfully.
110  * \retval Other return code    Operation failed with an error code.
111  */
112 int32_t tfm_ns_mailbox_client_call(uint32_t call_type,
113                                    const struct psa_client_params_t *params,
114                                    int32_t client_id,
115                                    int32_t *reply);
116 
117 #ifdef TFM_MULTI_CORE_NS_OS_MAILBOX_THREAD
118 /**
119  * \brief Handling PSA client calls in a dedicated NS mailbox thread.
120  *        This function constructs NS mailbox messages, transmits them to SPE
121  *        mailbox and returns the results to NS PSA client.
122  *
123  * \param[in] args              The pointer to the structure of PSA client call
124  *                              parameters.
125  */
126 void tfm_ns_mailbox_thread_runner(void *args);
127 #else /* TFM_MULTI_CORE_NS_OS_MAILBOX_THREAD */
128 #define tfm_ns_mailbox_thread_runner(args)        do {} while (0)
129 #endif /* TFM_MULTI_CORE_NS_OS_MAILBOX_THREAD */
130 
131 /**
132  * \brief Platform specific NSPE mailbox initialization.
133  *        Invoked by \ref tfm_ns_mailbox_init().
134  *
135  * \param[in] queue             The base address of NSPE mailbox queue to be
136  *                              initialized.
137  *
138  * \retval MAILBOX_SUCCESS      Operation succeeded.
139  * \retval Other return code    Operation failed with an error code.
140  */
141 int32_t tfm_ns_mailbox_hal_init(struct ns_mailbox_queue_t *queue);
142 
143 /**
144  * \brief Notify SPE to deal with the PSA client call sent via mailbox
145  *
146  * \note The implementation depends on platform specific hardware and use case.
147  *
148  * \retval MAILBOX_SUCCESS      Operation succeeded.
149  * \retval Other return code    Operation failed with an error code.
150  */
151 int32_t tfm_ns_mailbox_hal_notify_peer(void);
152 
153 /**
154  * \brief Enter critical section of NSPE mailbox.
155  *
156  * Is used to lock data shared between cores running secure and non-secure side.
157  * Should not be used to lock non-secure data that are not shared with secure core.
158  *
159  * \note The implementation depends on platform specific hardware and use case.
160  *
161  * \return Platform specific critical section state. Use it to exit from critical section
162  * by passing result to \ref tfm_ns_mailbox_hal_exit_critical.
163  */
164 uint32_t tfm_ns_mailbox_hal_enter_critical(void);
165 
166 /**
167  * \brief Exit critical section of NSPE mailbox.
168  *
169  * \note The implementation depends on platform specific hardware and use case.
170  *
171  * \param[in] state             Critical section state returned by
172  *                              \ref tfm_ns_mailbox_hal_enter_critical.
173  */
174 void tfm_ns_mailbox_hal_exit_critical(uint32_t state);
175 
176 /**
177  * \brief Enter critical section of NSPE mailbox in IRQ handler.
178  *
179  * Is used to lock data shared between cores running secure and non-secure side.
180  * Should not be used to lock non-secure data that are not shared with secure core.
181  *
182  * \note The implementation depends on platform specific hardware and use case.
183  *
184  * \return Platform specific critical section state. Use it to exit from critical section
185  * by passing result to \ref tfm_ns_mailbox_hal_exit_critical_isr.
186  */
187 uint32_t tfm_ns_mailbox_hal_enter_critical_isr(void);
188 
189 /**
190  * \brief Enter critical section of NSPE mailbox in IRQ handler
191  *
192  * \note The implementation depends on platform specific hardware and use case.
193  *
194  * \param[in] state             Critical section state returned by
195  *                              \ref tfm_ns_mailbox_hal_enter_critical_isr.
196  */
197 void tfm_ns_mailbox_hal_exit_critical_isr(uint32_t state);
198 
199 #ifdef TFM_MULTI_CORE_NS_OS
200 /**
201  * \brief Initialize the multi-core lock for synchronizing PSA client call(s)
202  *        The actual implementation depends on the non-secure use scenario.
203  *
204  * \return \ref MAILBOX_SUCCESS on success
205  * \return \ref MAILBOX_GENERIC_ERROR on error
206  */
207 int32_t tfm_ns_mailbox_os_lock_init(void);
208 
209 /**
210  * \brief Acquire the multi-core lock for synchronizing PSA client call(s)
211  *        The actual implementation depends on the non-secure use scenario.
212  *
213  * \return \ref MAILBOX_SUCCESS on success
214  * \return \ref MAILBOX_GENERIC_ERROR on error
215  */
216 int32_t tfm_ns_mailbox_os_lock_acquire(void);
217 
218 /**
219  * \brief Release the multi-core lock for synchronizing PSA client call(s)
220  *        The actual implementation depends on the non-secure use scenario.
221  *
222  * \return \ref MAILBOX_SUCCESS on success
223  * \return \ref MAILBOX_GENERIC_ERROR on error
224  */
225 int32_t tfm_ns_mailbox_os_lock_release(void);
226 
227 /**
228  * \brief Get the handle of the current non-secure task executing mailbox
229  *        functionalities
230  *
231  * \note This function should be implemented according to NS OS and
232  *       actual use scenario.
233  *       This function can be ignored or return NULL if sleep/wake-up mechanism
234  *       is not required in PSA Client API implementation.
235  *
236  * \return Return the handle of task.
237  */
238 const void *tfm_ns_mailbox_os_get_task_handle(void);
239 
240 /**
241  * \brief Performs use scenario and NS OS specific waiting mechanism to wait for
242  *        the reply to be returned from SPE.
243  *
244  * \note This function is implemented by NS OS specific waiting mechanism
245  *       according to use scenario.
246  */
247 void tfm_ns_mailbox_os_wait_reply(void);
248 
249 /*
250  * \brief Performs use scenario and NS OS specific mechanism in a mailbox IRQ
251  *        handler, to wake up a sleeping task which is waiting for its mailbox
252  *        message reply.
253  *
254  * \note The underlying NS OS specific function called inside this function
255  *       should be able to work in an IRQ handler.
256  *
257  * \note This function is implemented by NS OS specific waiting
258  *       mechanism according to use scenario.
259  *
260  * \param[in] task_handle       The handle to the task to be woken up.
261  */
262 void tfm_ns_mailbox_os_wake_task_isr(const void *task_handle);
263 
264 /**
265  * \brief Create and initialize a message queue
266  *
267  * \param[in] msg_size        The maximum message size in bytes
268  * \param[in] msg_count       The maximum number of messages in queue
269  *
270  * \return Returns handle of the message queue created, or NULL in case of error
271  */
272 void *tfm_ns_mailbox_os_mq_create(size_t msg_size, uint8_t msg_count);
273 
274 /**
275  * \brief Send a request via message queue
276  *
277  * \param[in] mq_handle       The handle of message queue
278  * \param[in] msg_ptr         The pointer to the message to be sent
279  *
280  * \note The message size must be the same as the value set in
281  *       \ref tfm_ns_mailbox_os_mq_create.
282  *
283  * \return \ref MAILBOX_SUCCESS if the message is successfully sent, or
284  *         other return code in case of error
285  */
286 int32_t tfm_ns_mailbox_os_mq_send(void *mq_handle, const void *msg_ptr);
287 
288 /**
289  * \brief Receive a request from message queue
290  *
291  * \param[in] mq_handle       The handle of message queue
292  * \param[in] msg_ptr         The pointer to buffer for message to be received
293  *
294  * \return \ref MAILBOX_SUCCESS if the message is successfully received, or
295  *         other return code in case of error
296  *
297  * \note The message size is the same as the value set in
298  *       \ref tfm_ns_mailbox_os_mq_create.
299  *
300  * \note The function should be blocked until a message is received from message
301  *       queue, unless a fatal error occurs.
302  */
303 int32_t tfm_ns_mailbox_os_mq_receive(void *mq_handle, void *msg_ptr);
304 
305 /**
306  * \brief Go through mailbox messages already replied by SPE mailbox and
307  *        wake up the owner tasks of replied mailbox messages.
308  *        This function is intended to be called inside platform specific
309  *        notification IRQ handler.
310  *
311  * \return MAILBOX_SUCCESS       The tasks of replied mailbox messages
312  *                               were found and wake-up signals were sent.
313  * \return MAILBOX_NO_PEND_EVENT No replied mailbox message is found.
314  * \return Other return code     Failed with an error code
315  */
316 int32_t tfm_ns_mailbox_wake_reply_owner_isr(void);
317 
318 /**
319  * \brief NSPE local mailbox spin lock.
320  *        Implemented by NS RTOS specific implementation.
321  */
322 void tfm_ns_mailbox_os_spin_lock(void);
323 
324 /**
325  * \brief NSPE local mailbox spin unlock.
326  *        Implemented by NS RTOS specific implementation.
327  */
328 void tfm_ns_mailbox_os_spin_unlock(void);
329 #else /* TFM_MULTI_CORE_NS_OS */
330 #define tfm_ns_mailbox_os_wait_reply()         do {} while (0)
331 
332 #define tfm_ns_mailbox_os_wake_task_isr(task)  do {} while (0)
333 
tfm_ns_mailbox_os_lock_init(void)334 static inline int32_t tfm_ns_mailbox_os_lock_init(void)
335 {
336     return MAILBOX_SUCCESS;
337 }
338 
tfm_ns_mailbox_os_lock_acquire(void)339 static inline int32_t tfm_ns_mailbox_os_lock_acquire(void)
340 {
341     return MAILBOX_SUCCESS;
342 }
343 
tfm_ns_mailbox_os_lock_release(void)344 static inline int32_t tfm_ns_mailbox_os_lock_release(void)
345 {
346     return MAILBOX_SUCCESS;
347 }
348 
tfm_ns_mailbox_os_get_task_handle(void)349 static inline const void *tfm_ns_mailbox_os_get_task_handle(void)
350 {
351     return NULL;
352 }
353 
tfm_ns_mailbox_wake_reply_owner_isr(void)354 static inline int32_t tfm_ns_mailbox_wake_reply_owner_isr(void)
355 {
356     return MAILBOX_NO_PEND_EVENT;
357 }
358 
359 /*
360  * Local spinlock is implemented as a dummy one when integrating with NS bare
361  * metal environment since interrupt is not required in NS mailbox.
362  */
363 #define tfm_ns_mailbox_os_spin_lock()   do {} while (0)
364 
365 #define tfm_ns_mailbox_os_spin_unlock() do {} while (0)
366 #endif /* TFM_MULTI_CORE_NS_OS */
367 
368 /* The following inline functions configure non-secure mailbox queue status */
clear_queue_slot_empty(struct ns_mailbox_queue_t * queue_ptr,uint8_t idx)369 static inline void clear_queue_slot_empty(struct ns_mailbox_queue_t *queue_ptr,
370                                           uint8_t idx)
371 {
372     if (idx < NUM_MAILBOX_QUEUE_SLOT) {
373         queue_ptr->empty_slots &= ~(1UL << idx);
374     }
375 }
376 
set_queue_slot_pend(struct ns_mailbox_queue_t * queue_ptr,uint8_t idx)377 static inline void set_queue_slot_pend(struct ns_mailbox_queue_t *queue_ptr,
378                                        uint8_t idx)
379 {
380     if (idx < NUM_MAILBOX_QUEUE_SLOT) {
381         MAILBOX_INVALIDATE_CACHE(&queue_ptr->status,
382                                  sizeof(queue_ptr->status));
383         queue_ptr->status.pend_slots |= (1UL << idx);
384         MAILBOX_CLEAN_CACHE(&queue_ptr->status,
385                             sizeof(queue_ptr->status));
386     }
387 }
388 
clear_queue_slot_all_replied(struct ns_mailbox_queue_t * queue_ptr)389 static inline mailbox_queue_status_t clear_queue_slot_all_replied(
390                                            struct ns_mailbox_queue_t *queue_ptr)
391 {
392     mailbox_queue_status_t status;
393 
394     MAILBOX_INVALIDATE_CACHE(&queue_ptr->status,
395                              sizeof(queue_ptr->status));
396     status = queue_ptr->status.replied_slots;
397     queue_ptr->status.replied_slots = 0;
398     MAILBOX_CLEAN_CACHE(&queue_ptr->status,
399                         sizeof(queue_ptr->status));
400     return status;
401 }
402 
403 /**
404  * \brief Set the NS mailbox queue in the respective mailbox implementation.
405  */
406 void mailbox_set_queue_ptr(struct ns_mailbox_queue_t *queue);
407 
408 #ifdef __cplusplus
409 }
410 #endif
411 
412 #endif /* __TFM_NS_MAILBOX_H__ */
413