TF-M Reference Manual  1.2.0
TrustedFirmware-M
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tfm_ns_mailbox.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include <string.h>
9 
10 #include "cmsis_compiler.h"
11 #include "tfm_ns_mailbox.h"
12 #include "tfm_plat_ns.h"
13 
14 /* The pointer to NSPE mailbox queue */
15 static struct ns_mailbox_queue_t *mailbox_queue_ptr = NULL;
16 
17 static inline void clear_queue_slot_empty(uint8_t idx)
18 {
19  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
20  mailbox_queue_ptr->empty_slots &= ~(1 << idx);
21  }
22 }
23 
24 static inline void set_queue_slot_empty(uint8_t idx)
25 {
26  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
27  mailbox_queue_ptr->empty_slots |= (1 << idx);
28  }
29 }
30 
31 static inline void set_queue_slot_pend(uint8_t idx)
32 {
33  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
34  mailbox_queue_ptr->pend_slots |= (1 << idx);
35  }
36 }
37 
38 static inline int32_t get_mailbox_msg_handle(uint8_t idx,
39  mailbox_msg_handle_t *handle)
40 {
41  if ((idx >= NUM_MAILBOX_QUEUE_SLOT) || !handle) {
42  return MAILBOX_INVAL_PARAMS;
43  }
44 
45  *handle = (mailbox_msg_handle_t)(idx + 1);
46 
47  return MAILBOX_SUCCESS;
48 }
49 
50 static inline int32_t get_mailbox_msg_idx(mailbox_msg_handle_t handle,
51  uint8_t *idx)
52 {
53  if ((handle == MAILBOX_MSG_NULL_HANDLE) || !idx) {
54  return MAILBOX_INVAL_PARAMS;
55  }
56 
57  *idx = (uint8_t)(handle - 1);
58 
59  return MAILBOX_SUCCESS;
60 }
61 
62 static inline void clear_queue_slot_replied(uint8_t idx)
63 {
64  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
65  mailbox_queue_ptr->replied_slots &= ~(1 << idx);
66  }
67 }
68 
69 static inline void set_queue_slot_woken(uint8_t idx)
70 {
71  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
72  mailbox_queue_ptr->queue[idx].is_woken = true;
73  }
74 }
75 
76 static inline bool is_queue_slot_woken(uint8_t idx)
77 {
78  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
79  return mailbox_queue_ptr->queue[idx].is_woken;
80  }
81 
82  return false;
83 }
84 
85 static inline void clear_queue_slot_woken(uint8_t idx)
86 {
87  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
88  mailbox_queue_ptr->queue[idx].is_woken = false;
89  }
90 }
91 
92 static uint8_t acquire_empty_slot(const struct ns_mailbox_queue_t *queue)
93 {
94  uint8_t idx;
96 
98  status = queue->empty_slots;
99 
100  if (!status) {
101  /* No empty slot */
103  return NUM_MAILBOX_QUEUE_SLOT;
104  }
105 
106  for (idx = 0; idx < NUM_MAILBOX_QUEUE_SLOT; idx++) {
107  if (status & (1 << idx)) {
108  break;
109  }
110  }
111 
112  clear_queue_slot_empty(idx);
113 
115 
116  return idx;
117 }
118 
119 static void set_msg_owner(uint8_t idx, const void *owner)
120 {
121  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
122  mailbox_queue_ptr->queue[idx].owner = owner;
123  }
124 }
125 
126 #ifdef TFM_MULTI_CORE_TEST
127 /*
128  * When NSPE mailbox only covers a single non-secure core, spinlock is only
129  * required to disable IRQ.
130  */
131 static inline void ns_mailbox_spin_lock(void)
132 {
133  __disable_irq();
134 }
135 
136 static inline void ns_mailbox_spin_unlock(void)
137 {
138  __enable_irq();
139 }
140 
141 void tfm_ns_mailbox_tx_stats_init(void)
142 {
143  if (!mailbox_queue_ptr) {
144  return;
145  }
146 
147  mailbox_queue_ptr->nr_tx = 0;
148  mailbox_queue_ptr->nr_used_slots = 0;
149 }
150 
151 static void mailbox_tx_stats_update(struct ns_mailbox_queue_t *ns_queue)
152 {
153  mailbox_queue_status_t empty_status;
154  uint8_t idx, nr_empty = 0;
155 
156  if (!ns_queue) {
157  return;
158  }
159 
161  /* Count the number of used slots when this tx arrives */
162  empty_status = ns_queue->empty_slots;
164 
165  if (empty_status) {
166  for (idx = 0; idx < NUM_MAILBOX_QUEUE_SLOT; idx++) {
167  if (empty_status & (0x1UL << idx)) {
168  nr_empty++;
169  }
170  }
171  }
172 
173  ns_mailbox_spin_lock();
174  ns_queue->nr_used_slots += (NUM_MAILBOX_QUEUE_SLOT - nr_empty);
175  ns_queue->nr_tx++;
176  ns_mailbox_spin_unlock();
177 }
178 
179 void tfm_ns_mailbox_stats_avg_slot(struct ns_mailbox_stats_res_t *stats_res)
180 {
181  uint32_t nr_used_slots, nr_tx;
182 
183  if (!mailbox_queue_ptr || !stats_res) {
184  return;
185  }
186 
187  nr_used_slots = mailbox_queue_ptr->nr_used_slots;
188  nr_tx = mailbox_queue_ptr->nr_tx;
189 
190  stats_res->avg_nr_slots = nr_used_slots / nr_tx;
191  nr_used_slots %= nr_tx;
192  stats_res->avg_nr_slots_tenths = nr_used_slots * 10 / nr_tx;
193 }
194 #endif
195 
197  const struct psa_client_params_t *params,
198  int32_t client_id)
199 {
200  uint8_t idx;
201  struct mailbox_msg_t *msg_ptr;
202  mailbox_msg_handle_t handle;
203  const void *task_handle;
204 
205  if (!mailbox_queue_ptr) {
207  }
208 
209  if (!params) {
211  }
212 
213  idx = acquire_empty_slot(mailbox_queue_ptr);
214  if (idx >= NUM_MAILBOX_QUEUE_SLOT) {
215  return MAILBOX_QUEUE_FULL;
216  }
217 
218 #ifdef TFM_MULTI_CORE_TEST
219  mailbox_tx_stats_update(mailbox_queue_ptr);
220 #endif
221 
222  /* Fill the mailbox message */
223  msg_ptr = &mailbox_queue_ptr->queue[idx].msg;
224 
225  msg_ptr->call_type = call_type;
226  memcpy(&msg_ptr->params, params, sizeof(msg_ptr->params));
227  msg_ptr->client_id = client_id;
228 
229  /*
230  * Fetch the current task handle. The task will be woken up according the
231  * handle value set in the owner field.
232  */
233  task_handle = tfm_ns_mailbox_get_task_handle();
234  set_msg_owner(idx, task_handle);
235 
236  get_mailbox_msg_handle(idx, &handle);
237 
239  set_queue_slot_pend(idx);
241 
243 
244  return handle;
245 }
246 
248  int32_t *reply)
249 {
250  uint8_t idx;
251  int32_t ret;
252 
253  if (!mailbox_queue_ptr) {
254  return MAILBOX_INVAL_PARAMS;
255  }
256 
257  if ((handle == MAILBOX_MSG_NULL_HANDLE) || (!reply)) {
258  return MAILBOX_INVAL_PARAMS;
259  }
260 
261  ret = get_mailbox_msg_idx(handle, &idx);
262  if (ret != MAILBOX_SUCCESS) {
263  return ret;
264  }
265 
266  *reply = mailbox_queue_ptr->queue[idx].reply.return_val;
267 
268  /* Clear up the owner field */
269  set_msg_owner(idx, NULL);
270 
272  clear_queue_slot_replied(idx);
273  clear_queue_slot_woken(idx);
274  /*
275  * Make sure that the empty flag is set after all the other status flags are
276  * re-initialized.
277  */
278  set_queue_slot_empty(idx);
280 
281  return MAILBOX_SUCCESS;
282 }
283 
285 {
286  uint8_t idx;
287  int32_t ret;
288  mailbox_queue_status_t status;
289 
290  if (!mailbox_queue_ptr) {
291  return false;
292  }
293 
294  if (handle == MAILBOX_MSG_NULL_HANDLE) {
295  return false;
296  }
297 
298  ret = get_mailbox_msg_idx(handle, &idx);
299  if (ret != MAILBOX_SUCCESS) {
300  return false;
301  }
302 
304  status = mailbox_queue_ptr->replied_slots;
306 
307  if (status & (1 << idx)) {
308  return true;
309  }
310 
311  return false;
312 }
313 
315 {
316  uint8_t idx;
317  mailbox_msg_handle_t handle;
318  mailbox_queue_status_t replied_status;
319 
320  if (!mailbox_queue_ptr) {
322  }
323 
325  replied_status = mailbox_queue_ptr->replied_slots;
327 
328  if (!replied_status) {
330  }
331 
332  for (idx = 0; idx < NUM_MAILBOX_QUEUE_SLOT; idx++) {
333  /* Find the first replied message in queue */
334  if (replied_status & (0x1UL << idx)) {
336  clear_queue_slot_replied(idx);
337  set_queue_slot_woken(idx);
339 
340  if (get_mailbox_msg_handle(idx, &handle) == MAILBOX_SUCCESS) {
341  return handle;
342  }
343  }
344  }
345 
347 }
348 
350 {
351  uint8_t idx;
352 
353  if (get_mailbox_msg_idx(handle, &idx) != MAILBOX_SUCCESS) {
354  return NULL;
355  }
356 
357  if (idx < NUM_MAILBOX_QUEUE_SLOT) {
358  return mailbox_queue_ptr->queue[idx].owner;
359  }
360 
361  return NULL;
362 }
363 
365 {
366  int32_t ret;
367 
368  if (!queue) {
369  return MAILBOX_INVAL_PARAMS;
370  }
371 
372  /*
373  * Further verification of mailbox queue address may be required according
374  * to non-secure memory assignment.
375  */
376 
377  memset(queue, 0, sizeof(*queue));
378 
379  /* Initialize empty bitmask */
380  queue->empty_slots =
381  (mailbox_queue_status_t)((1UL << (NUM_MAILBOX_QUEUE_SLOT - 1)) - 1);
382  queue->empty_slots +=
383  (mailbox_queue_status_t)(1UL << (NUM_MAILBOX_QUEUE_SLOT - 1));
384 
385  mailbox_queue_ptr = queue;
386 
387  /* Platform specific initialization. */
388  ret = tfm_ns_mailbox_hal_init(queue);
389 
390 #ifdef TFM_MULTI_CORE_TEST
391  tfm_ns_mailbox_tx_stats_init();
392 #endif
393 
394  return ret;
395 }
396 
397 #ifdef TFM_MULTI_CORE_MULTI_CLIENT_CALL
398 int32_t tfm_ns_mailbox_wait_reply(mailbox_msg_handle_t handle)
399 {
400  uint8_t idx;
401  int32_t ret;
402 
403  if (!mailbox_queue_ptr) {
404  return MAILBOX_INVAL_PARAMS;
405  }
406 
407  if (handle == MAILBOX_MSG_NULL_HANDLE) {
408  return MAILBOX_INVAL_PARAMS;
409  }
410 
411  ret = get_mailbox_msg_idx(handle, &idx);
412  if (ret != MAILBOX_SUCCESS) {
413  return ret;
414  }
415 
416  while (1) {
417  tfm_ns_mailbox_hal_wait_reply(handle);
418 
419  /*
420  * Woken up from sleep
421  * Check the completed flag to make sure that the current thread is
422  * woken up by reply event, rather than other events.
423  */
425  if (is_queue_slot_woken(idx)) {
427  break;
428  }
430  }
431 
432  return MAILBOX_SUCCESS;
433 }
434 #endif
mailbox_msg_handle_t tfm_ns_mailbox_tx_client_req(uint32_t call_type, const struct psa_client_params_t *params, int32_t client_id)
Prepare and send PSA client request to SPE via mailbox.
struct mailbox_reply_t reply
Definition: tfm_mailbox.h:137
mailbox_queue_status_t replied_slots
Definition: tfm_mailbox.h:155
#define MAILBOX_INVAL_PARAMS
Definition: tfm_mailbox.h:70
struct psa_client_params_t params
Definition: tfm_mailbox.h:109
mailbox_queue_status_t empty_slots
Definition: tfm_mailbox.h:151
int32_t tfm_ns_mailbox_hal_notify_peer(void)
Notify SPE to deal with the PSA client call sent via mailbox.
#define MAILBOX_SUCCESS
Definition: tfm_mailbox.h:68
uint32_t call_type
Definition: tfm_mailbox.h:108
mailbox_queue_status_t pend_slots
Definition: tfm_mailbox.h:152
int32_t client_id
Definition: tfm_mailbox.h:113
mailbox_msg_handle_t tfm_ns_mailbox_fetch_reply_msg_isr(void)
Fetch the handle to the first replied mailbox message in the NSPE mailbox queue. This function is int...
int32_t tfm_ns_mailbox_init(struct ns_mailbox_queue_t *queue)
NSPE mailbox initialization.
struct ns_mailbox_slot_t queue[(1)]
Definition: tfm_mailbox.h:160
#define NUM_MAILBOX_QUEUE_SLOT
Definition: tfm_mailbox.h:57
void tfm_ns_mailbox_hal_enter_critical(void)
Enter critical section of NSPE mailbox.
uint32_t mailbox_queue_status_t
Definition: tfm_mailbox.h:147
#define MAILBOX_QUEUE_FULL
Definition: tfm_mailbox.h:69
int32_t mailbox_msg_handle_t
Definition: tfm_mailbox.h:122
void tfm_ns_mailbox_hal_exit_critical_isr(void)
Enter critical section of NSPE mailbox in IRQ handler.
int32_t tfm_ns_mailbox_rx_client_reply(mailbox_msg_handle_t handle, int32_t *reply)
Fetch PSA client return result.
bool tfm_ns_mailbox_is_msg_replied(mailbox_msg_handle_t handle)
Check whether a specific mailbox message has been replied.
void tfm_ns_mailbox_hal_enter_critical_isr(void)
Enter critical section of NSPE mailbox in IRQ handler.
int32_t return_val
Definition: tfm_mailbox.h:131
void * memcpy(void *dest, const void *src, size_t n)
Definition: crt_memcpy.c:10
int32_t tfm_ns_mailbox_hal_init(struct ns_mailbox_queue_t *queue)
Platform specific NSPE mailbox initialization. Invoked by tfm_ns_mailbox_init().
struct mailbox_msg_t msg
Definition: tfm_mailbox.h:136
#define MAILBOX_MSG_NULL_HANDLE
Definition: tfm_mailbox.h:124
const void * tfm_ns_mailbox_get_msg_owner(mailbox_msg_handle_t handle)
Return the handle of owner task of a mailbox message according to the mailbox_msg_handle_t.
void tfm_ns_mailbox_hal_exit_critical(void)
Exit critical section of NSPE mailbox.
void * memset(void *s, int c, size_t n)
Definition: crt_memset.c:10
const void * owner
Definition: tfm_mailbox.h:138