1 /*
2 * Copyright (c) 2019-2022, Arm Limited. All rights reserved.
3 * Copyright (c) 2021, Cypress Semiconductor Corporation. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 */
8
9 #include "cmsis_compiler.h"
10
11 #include "psa/error.h"
12 #include "utilities.h"
13 #include "tfm_arch.h"
14 #include "thread.h"
15 #include "tfm_spe_mailbox.h"
16 #include "tfm_rpc.h"
17 #include "tfm_multi_core.h"
18
19 static struct secure_mailbox_queue_t spe_mailbox_queue;
20
tfm_mailbox_dispatch(uint32_t call_type,const struct psa_client_params_t * params,int32_t client_id,psa_status_t * psa_ret)21 static int32_t tfm_mailbox_dispatch(uint32_t call_type,
22 const struct psa_client_params_t *params,
23 int32_t client_id,
24 psa_status_t *psa_ret)
25 {
26 struct client_call_params_t spm_params = {0};
27
28 SPM_ASSERT(params != NULL);
29 SPM_ASSERT(psa_ret != NULL);
30
31 (void)client_id;
32
33 switch (call_type) {
34 case MAILBOX_PSA_FRAMEWORK_VERSION:
35 *psa_ret = tfm_rpc_psa_framework_version();
36 return MAILBOX_SUCCESS;
37 case MAILBOX_PSA_VERSION:
38 spm_params.sid = params->psa_version_params.sid;
39 *psa_ret = tfm_rpc_psa_version(&spm_params);
40 return MAILBOX_SUCCESS;
41 case MAILBOX_PSA_CALL:
42 spm_params.handle = params->psa_call_params.handle;
43 spm_params.type = params->psa_call_params.type;
44 spm_params.in_vec = params->psa_call_params.in_vec;
45 spm_params.in_len = params->psa_call_params.in_len;
46 spm_params.out_vec = params->psa_call_params.out_vec;
47 spm_params.out_len = params->psa_call_params.out_len;
48 *psa_ret = tfm_rpc_psa_call(&spm_params);
49 return MAILBOX_SUCCESS;
50 /* Following cases are only needed by connection-based services */
51 #if CONFIG_TFM_CONNECTION_BASED_SERVICE_API == 1
52 case MAILBOX_PSA_CONNECT:
53 spm_params.sid = params->psa_connect_params.sid;
54 spm_params.version = params->psa_connect_params.version;
55 *psa_ret = tfm_rpc_psa_connect(&spm_params);
56 return MAILBOX_SUCCESS;
57 case MAILBOX_PSA_CLOSE:
58 spm_params.handle = params->psa_close_params.handle;
59 tfm_rpc_psa_close(&spm_params);
60 return MAILBOX_SUCCESS;
61 #endif /* CONFIG_TFM_CONNECTION_BASED_SERVICE_API */
62 default:
63 return MAILBOX_INVAL_PARAMS;
64 }
65 }
66
set_spe_queue_empty_status(uint8_t idx)67 __STATIC_INLINE void set_spe_queue_empty_status(uint8_t idx)
68 {
69 if (idx < NUM_MAILBOX_QUEUE_SLOT) {
70 spe_mailbox_queue.empty_slots |= (1 << idx);
71 }
72 }
73
clear_spe_queue_empty_status(uint8_t idx)74 __STATIC_INLINE void clear_spe_queue_empty_status(uint8_t idx)
75 {
76 if (idx < NUM_MAILBOX_QUEUE_SLOT) {
77 spe_mailbox_queue.empty_slots &= ~(1 << idx);
78 }
79 }
80
get_spe_queue_empty_status(uint8_t idx)81 __STATIC_INLINE bool get_spe_queue_empty_status(uint8_t idx)
82 {
83 if ((idx < NUM_MAILBOX_QUEUE_SLOT) &&
84 (spe_mailbox_queue.empty_slots & (1 << idx))) {
85 return true;
86 }
87
88 return false;
89 }
90
get_nspe_queue_pend_status(const struct ns_mailbox_queue_t * ns_queue)91 __STATIC_INLINE mailbox_queue_status_t get_nspe_queue_pend_status(
92 const struct ns_mailbox_queue_t *ns_queue)
93 {
94 return ns_queue->pend_slots;
95 }
96
set_nspe_queue_replied_status(struct ns_mailbox_queue_t * ns_queue,mailbox_queue_status_t mask)97 __STATIC_INLINE void set_nspe_queue_replied_status(
98 struct ns_mailbox_queue_t *ns_queue,
99 mailbox_queue_status_t mask)
100 {
101 ns_queue->replied_slots |= mask;
102 }
103
clear_nspe_queue_pend_status(struct ns_mailbox_queue_t * ns_queue,mailbox_queue_status_t mask)104 __STATIC_INLINE void clear_nspe_queue_pend_status(
105 struct ns_mailbox_queue_t *ns_queue,
106 mailbox_queue_status_t mask)
107 {
108 ns_queue->pend_slots &= ~mask;
109 }
110
get_spe_mailbox_msg_handle(uint8_t idx,mailbox_msg_handle_t * handle)111 __STATIC_INLINE int32_t get_spe_mailbox_msg_handle(uint8_t idx,
112 mailbox_msg_handle_t *handle)
113 {
114 if ((idx >= NUM_MAILBOX_QUEUE_SLOT) || !handle) {
115 return MAILBOX_INVAL_PARAMS;
116 }
117
118 *handle = (mailbox_msg_handle_t)(idx + 1);
119
120 return MAILBOX_SUCCESS;
121 }
122
get_spe_mailbox_msg_idx(mailbox_msg_handle_t handle,uint8_t * idx)123 __STATIC_INLINE int32_t get_spe_mailbox_msg_idx(mailbox_msg_handle_t handle,
124 uint8_t *idx)
125 {
126 if ((handle == MAILBOX_MSG_NULL_HANDLE) || !idx) {
127 return MAILBOX_INVAL_PARAMS;
128 }
129
130 *idx = (uint8_t)(handle - 1);
131
132 return MAILBOX_SUCCESS;
133 }
134
mailbox_clean_queue_slot(uint8_t idx)135 static void mailbox_clean_queue_slot(uint8_t idx)
136 {
137 if (idx >= NUM_MAILBOX_QUEUE_SLOT) {
138 return;
139 }
140
141 spm_memset(&spe_mailbox_queue.queue[idx], 0,
142 sizeof(spe_mailbox_queue.queue[idx]));
143 set_spe_queue_empty_status(idx);
144 }
145
get_nspe_reply_addr(uint8_t idx)146 __STATIC_INLINE struct mailbox_reply_t *get_nspe_reply_addr(uint8_t idx)
147 {
148 uint8_t ns_slot_idx;
149
150 if (idx >= NUM_MAILBOX_QUEUE_SLOT) {
151 return NULL;
152 }
153
154 ns_slot_idx = spe_mailbox_queue.queue[idx].ns_slot_idx;
155
156 return &spe_mailbox_queue.ns_queue->queue[ns_slot_idx].reply;
157 }
158
mailbox_direct_reply(uint8_t idx,uint32_t result)159 static void mailbox_direct_reply(uint8_t idx, uint32_t result)
160 {
161 struct mailbox_reply_t *reply_ptr;
162 uint32_t ret_result = result;
163
164 /* Get reply address */
165 reply_ptr = get_nspe_reply_addr(idx);
166 spm_memcpy(&reply_ptr->return_val, &ret_result,
167 sizeof(reply_ptr->return_val));
168
169 mailbox_clean_queue_slot(idx);
170
171 /*
172 * Skip NSPE queue status update after single reply.
173 * Update NSPE queue status after all the mailbox messages are completed
174 */
175 }
176
check_mailbox_msg(const struct mailbox_msg_t * msg)177 __STATIC_INLINE int32_t check_mailbox_msg(const struct mailbox_msg_t *msg)
178 {
179 /*
180 * TODO
181 * Comprehensive check of mailbox msessage content can be implemented here.
182 */
183 (void)msg;
184 return MAILBOX_SUCCESS;
185 }
186
tfm_mailbox_handle_msg(void)187 int32_t tfm_mailbox_handle_msg(void)
188 {
189 uint8_t idx;
190 int32_t result;
191 psa_status_t psa_ret = PSA_ERROR_GENERIC_ERROR;
192 mailbox_queue_status_t mask_bits, pend_slots, reply_slots = 0;
193 struct ns_mailbox_queue_t *ns_queue = spe_mailbox_queue.ns_queue;
194 struct mailbox_msg_t *msg_ptr;
195
196 SPM_ASSERT(ns_queue != NULL);
197
198 tfm_mailbox_hal_enter_critical();
199
200 pend_slots = get_nspe_queue_pend_status(ns_queue);
201
202 tfm_mailbox_hal_exit_critical();
203
204 /* Check if NSPE mailbox did assert a PSA client call request */
205 if (!pend_slots) {
206 return MAILBOX_NO_PEND_EVENT;
207 }
208
209 for (idx = 0; idx < NUM_MAILBOX_QUEUE_SLOT; idx++) {
210 mask_bits = (1 << idx);
211 /* Check if current NSPE mailbox queue slot is pending for handling */
212 if (!(pend_slots & mask_bits)) {
213 continue;
214 }
215
216 /*
217 * TODO
218 * The operations are simplified here. Use the SPE mailbox queue
219 * slot with the same idx as that of the NSPE mailbox queue slot.
220 * A more general implementation should dynamically search and
221 * select an empty SPE mailbox queue slot.
222 */
223 clear_spe_queue_empty_status(idx);
224 spe_mailbox_queue.queue[idx].ns_slot_idx = idx;
225
226 msg_ptr = &spe_mailbox_queue.queue[idx].msg;
227 spm_memcpy(msg_ptr, &ns_queue->queue[idx].msg, sizeof(*msg_ptr));
228
229 if (check_mailbox_msg(msg_ptr) != MAILBOX_SUCCESS) {
230 mailbox_clean_queue_slot(idx);
231 continue;
232 }
233
234 get_spe_mailbox_msg_handle(idx,
235 &spe_mailbox_queue.queue[idx].msg_handle);
236
237 /*
238 * Set the current slot index under processing.
239 * The value is used in mailbox_get_caller_data() to identify the
240 * mailbox queue slot.
241 */
242 spe_mailbox_queue.cur_proc_slot_idx = idx;
243
244 result = tfm_mailbox_dispatch(msg_ptr->call_type, &msg_ptr->params,
245 msg_ptr->client_id, &psa_ret);
246 if (result != MAILBOX_SUCCESS) {
247 mailbox_clean_queue_slot(idx);
248 continue;
249 }
250
251 /* Clean up the current slot index under processing */
252 spe_mailbox_queue.cur_proc_slot_idx = NUM_MAILBOX_QUEUE_SLOT;
253
254 if ((msg_ptr->call_type == MAILBOX_PSA_FRAMEWORK_VERSION) ||
255 (msg_ptr->call_type == MAILBOX_PSA_VERSION)) {
256 /*
257 * Directly write the result to NSPE for psa_framework_version() and
258 * psa_version().
259 */
260 reply_slots |= (1 << idx);
261
262 mailbox_direct_reply(idx, (uint32_t)psa_ret);
263 } else if ((msg_ptr->call_type == MAILBOX_PSA_CONNECT) ||
264 (msg_ptr->call_type == MAILBOX_PSA_CALL)) {
265 /*
266 * If it failed to deliver psa_connect() or psa_call() request to
267 * TF-M IPC SPM, the failure result should be returned immediately.
268 */
269 if (psa_ret != PSA_SUCCESS) {
270 reply_slots |= (1 << idx);
271 mailbox_direct_reply(idx, (uint32_t)psa_ret);
272 }
273 }
274 /*
275 * Skip checking psa_call() since it neither returns immediately nor
276 * has return value.
277 */
278 }
279
280 tfm_mailbox_hal_enter_critical();
281
282 /* Clean the NSPE mailbox pending status. */
283 clear_nspe_queue_pend_status(ns_queue, pend_slots);
284
285 /* Set the NSPE mailbox replied status */
286 set_nspe_queue_replied_status(ns_queue, reply_slots);
287
288 tfm_mailbox_hal_exit_critical();
289
290 if (reply_slots) {
291 tfm_mailbox_hal_notify_peer();
292 }
293
294 return MAILBOX_SUCCESS;
295 }
296
tfm_mailbox_reply_msg(mailbox_msg_handle_t handle,int32_t reply)297 int32_t tfm_mailbox_reply_msg(mailbox_msg_handle_t handle, int32_t reply)
298 {
299 uint8_t idx;
300 int32_t ret;
301 struct ns_mailbox_queue_t *ns_queue = spe_mailbox_queue.ns_queue;
302
303 SPM_ASSERT(ns_queue != NULL);
304
305 /*
306 * If handle == MAILBOX_MSG_NULL_HANDLE, reply to the mailbox message
307 * in the first slot.
308 * When multiple ongoing PSA client calls from NSPE are supported,
309 * additional check might be necessary to avoid spoofing the first slot.
310 */
311 if (handle == MAILBOX_MSG_NULL_HANDLE) {
312 idx = 0;
313 } else {
314 ret = get_spe_mailbox_msg_idx(handle, &idx);
315 if (ret != MAILBOX_SUCCESS) {
316 return ret;
317 }
318 }
319
320 if (get_spe_queue_empty_status(idx)) {
321 return MAILBOX_NO_PEND_EVENT;
322 }
323
324 mailbox_direct_reply(idx, (uint32_t)reply);
325
326 tfm_mailbox_hal_enter_critical();
327
328 /* Set the NSPE mailbox replied status */
329 set_nspe_queue_replied_status(ns_queue, (1 << idx));
330
331 tfm_mailbox_hal_exit_critical();
332
333 tfm_mailbox_hal_notify_peer();
334
335 return MAILBOX_SUCCESS;
336 }
337
338 /* RPC handle_req() callback */
mailbox_handle_req(void)339 static void mailbox_handle_req(void)
340 {
341 (void)tfm_mailbox_handle_msg();
342 }
343
344 /* RPC reply() callback */
mailbox_reply(const void * owner,int32_t ret)345 static void mailbox_reply(const void *owner, int32_t ret)
346 {
347 mailbox_msg_handle_t handle = MAILBOX_MSG_NULL_HANDLE;
348
349 /* If the owner is specified */
350 if (owner) {
351 handle = *((mailbox_msg_handle_t *)owner);
352 }
353
354 (void)tfm_mailbox_reply_msg(handle, ret);
355 }
356
357 /* RPC get_caller_data() callback */
mailbox_get_caller_data(int32_t client_id)358 static const void *mailbox_get_caller_data(int32_t client_id)
359 {
360 uint8_t idx;
361
362 (void)client_id;
363
364 idx = spe_mailbox_queue.cur_proc_slot_idx;
365 if (idx < NUM_MAILBOX_QUEUE_SLOT) {
366 return (const void *)&spe_mailbox_queue.queue[idx].msg_handle;
367 }
368
369 return NULL;
370 }
371
372 /* Mailbox specific operations callback for TF-M RPC */
373 static const struct tfm_rpc_ops_t mailbox_rpc_ops = {
374 .handle_req = mailbox_handle_req,
375 .reply = mailbox_reply,
376 .get_caller_data = mailbox_get_caller_data,
377 };
378
tfm_mailbox_init(void)379 int32_t tfm_mailbox_init(void)
380 {
381 int32_t ret;
382
383 spm_memset(&spe_mailbox_queue, 0, sizeof(spe_mailbox_queue));
384
385 spe_mailbox_queue.empty_slots =
386 (mailbox_queue_status_t)((1UL << (NUM_MAILBOX_QUEUE_SLOT - 1)) - 1);
387 spe_mailbox_queue.empty_slots +=
388 (mailbox_queue_status_t)(1UL << (NUM_MAILBOX_QUEUE_SLOT - 1));
389
390 /* Register RPC callbacks */
391 ret = tfm_rpc_register_ops(&mailbox_rpc_ops);
392 if (ret != TFM_RPC_SUCCESS) {
393 return MAILBOX_CALLBACK_REG_ERROR;
394 }
395
396 /*
397 * Platform specific initialization.
398 * Initialize Inter-Processor Communication and achieve the base address of
399 * NSPE mailbox queue
400 */
401 ret = tfm_mailbox_hal_init(&spe_mailbox_queue);
402 if (ret != MAILBOX_SUCCESS) {
403 tfm_rpc_unregister_ops();
404
405 return ret;
406 }
407
408 return MAILBOX_SUCCESS;
409 }
410
tfm_inter_core_comm_init(void)411 int32_t tfm_inter_core_comm_init(void)
412 {
413 return tfm_mailbox_init();
414 }
415