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