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