1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2022-2024, Linaro Limited and Contributors. All rights
4 * reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 *
8 * Description:
9 * msg buffer device driver.
10 */
11
12 #include <stdbool.h>
13 #include <string.h>
14 #include <fwk_assert.h>
15 #include <fwk_interrupt.h>
16 #include <fwk_log.h>
17 #include <fwk_mm.h>
18 #include <fwk_macros.h>
19 #include <fwk_module.h>
20 #include <fwk_module_idx.h>
21 #include <fwk_status.h>
22 #include <mod_scmi.h>
23 #include <mod_msg_smt.h>
24
25 struct __attribute((packed)) mod_msg_smt_memory {
26 uint32_t message_header;
27 uint32_t payload[];
28 };
29
30 struct smt_channel_ctx {
31 /* Channel identifier */
32 fwk_id_t id;
33
34 /* Channel configuration data */
35 struct mod_msg_smt_channel_config *config;
36
37 /* Channel read and write cache memory areas */
38 struct mod_msg_smt_memory *in;
39 struct mod_msg_smt_memory *out;
40 size_t in_len;
41 size_t out_len;
42
43 /* Message processing in progrees flag */
44 volatile bool locked;
45
46 /* Maximum payload size of the channel */
47 size_t max_payload_size;
48
49 /* Driver entity identifier */
50 fwk_id_t driver_id;
51
52 /* SCMI module service bound to the channel */
53 fwk_id_t scmi_service_id;
54
55 /* Driver API */
56 struct mod_msg_smt_driver_ouput_api *driver_api;
57
58 /* SCMI service API */
59 struct mod_scmi_from_transport_api *scmi_api;
60
61 /* Flag indicating the mailbox is ready */
62 bool msg_smt_mailbox_ready;
63 };
64
65 struct smt_ctx {
66 /* Table of channel contexts */
67 struct smt_channel_ctx *channel_ctx_table;
68
69 /* Number of channels */
70 unsigned int channel_count;
71 };
72
73 static struct smt_ctx smt_ctx;
74
75 /*
76 * SCMI Transport API
77 */
smt_get_secure(fwk_id_t channel_id,bool * secure)78 static int smt_get_secure(fwk_id_t channel_id, bool *secure)
79 {
80 if (secure == NULL) {
81 fwk_assert(false);
82 return FWK_E_PARAM;
83 }
84
85 /* No msg mailbox is considered secure */
86 *secure = 0;
87
88 return FWK_SUCCESS;
89 }
90
smt_get_max_payload_size(fwk_id_t channel_id,size_t * size)91 static int smt_get_max_payload_size(fwk_id_t channel_id, size_t *size)
92 {
93 struct smt_channel_ctx *channel_ctx;
94
95 if (size == NULL) {
96 fwk_assert(false);
97 return FWK_E_PARAM;
98 }
99
100 channel_ctx =
101 &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
102
103 *size = channel_ctx->max_payload_size;
104
105 return FWK_SUCCESS;
106 }
107
smt_get_message_header(fwk_id_t channel_id,uint32_t * header)108 static int smt_get_message_header(fwk_id_t channel_id, uint32_t *header)
109 {
110 struct smt_channel_ctx *channel_ctx;
111
112 if (header == NULL) {
113 fwk_assert(false);
114 return FWK_E_PARAM;
115 }
116
117 channel_ctx =
118 &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
119
120 if (!channel_ctx->locked) {
121 return FWK_E_ACCESS;
122 }
123
124 *header = channel_ctx->in->message_header;
125
126 return FWK_SUCCESS;
127 }
128
smt_get_payload(fwk_id_t channel_id,const void ** payload,size_t * size)129 static int smt_get_payload(fwk_id_t channel_id,
130 const void **payload,
131 size_t *size)
132 {
133 struct smt_channel_ctx *channel_ctx;
134
135 if (payload == NULL) {
136 fwk_assert(false);
137 return FWK_E_PARAM;
138 }
139
140 channel_ctx =
141 &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
142
143 if (!channel_ctx->locked)
144 return FWK_E_ACCESS;
145
146 *payload = channel_ctx->in->payload;
147
148 if (size != NULL) {
149 *size = channel_ctx->in_len -
150 sizeof(channel_ctx->in->message_header);
151 }
152
153 return FWK_SUCCESS;
154 }
155
smt_write_payload(fwk_id_t channel_id,size_t offset,const void * payload,size_t size)156 static int smt_write_payload(fwk_id_t channel_id,
157 size_t offset,
158 const void *payload,
159 size_t size)
160 {
161 struct smt_channel_ctx *channel_ctx;
162
163 channel_ctx =
164 &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
165
166 if ((payload == NULL) ||
167 (offset > channel_ctx->max_payload_size) ||
168 (size > channel_ctx->max_payload_size) ||
169 ((offset + size) > channel_ctx->max_payload_size)) {
170
171 fwk_assert(false);
172 return FWK_E_PARAM;
173 }
174
175 if (!channel_ctx->locked)
176 return FWK_E_ACCESS;
177
178 memcpy(((uint8_t*)channel_ctx->out->payload) + offset, payload, size);
179
180 return FWK_SUCCESS;
181 }
182
smt_respond(fwk_id_t channel_id,const void * payload,size_t size)183 static int smt_respond(fwk_id_t channel_id, const void *payload, size_t size)
184 {
185 struct smt_channel_ctx *channel_ctx;
186 struct mod_msg_smt_memory *memory;
187
188 channel_ctx = &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
189 memory = ((struct mod_msg_smt_memory *) channel_ctx->out);
190
191 /* Copy the payload from either the write buffer or the payload parameter */
192 if (payload) {
193 memcpy(memory->payload, payload, size);
194 }
195
196 channel_ctx->locked = false;
197
198 channel_ctx->out_len = sizeof(memory->message_header) + size;
199
200 channel_ctx->driver_api->raise_notification(channel_ctx->driver_id, channel_ctx->out_len);
201
202 return FWK_SUCCESS;
203 }
204
smt_transmit(fwk_id_t channel_id,uint32_t message_header,const void * payload,size_t size,bool request_ack_by_interrupt)205 static int smt_transmit(fwk_id_t channel_id, uint32_t message_header,
206 const void *payload, size_t size, bool request_ack_by_interrupt)
207 {
208 struct smt_channel_ctx *channel_ctx;
209 struct mod_msg_smt_memory *memory;
210
211 if (payload == NULL) {
212 return FWK_E_DATA;
213 }
214
215 channel_ctx = &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
216 memory = ((struct mod_msg_smt_memory *) channel_ctx->out);
217
218 if (!channel_ctx->locked) {
219 return FWK_SUCCESS;
220 }
221
222 memory->message_header = message_header;
223
224 /* Copy the payload */
225 memcpy(memory->payload, payload, size);
226
227 channel_ctx->out_len = sizeof(memory->message_header) + size;
228
229 /* Release the channel */
230 channel_ctx->locked = false;
231
232 /* Notify the agent */
233 channel_ctx->driver_api->raise_notification(channel_ctx->driver_id, channel_ctx->out_len);
234
235 return FWK_SUCCESS;
236 }
237
238 static const struct mod_scmi_to_transport_api smt_mod_scmi_to_transport_api = {
239 .get_secure = smt_get_secure,
240 .get_max_payload_size = smt_get_max_payload_size,
241 .get_message_header = smt_get_message_header,
242 .get_payload = smt_get_payload,
243 .write_payload = smt_write_payload,
244 .respond = smt_respond,
245 .transmit = smt_transmit,
246 };
247
248 /*
249 * Driver handler API
250 */
smt_requester_handler(struct smt_channel_ctx * channel_ctx)251 static int smt_requester_handler(struct smt_channel_ctx *channel_ctx)
252 {
253 int status;
254
255 /* Commit to sending a response */
256 channel_ctx->locked = true;
257
258 /* Prepare answer and copy message header (Payload not copied) */
259 channel_ctx->out->message_header = channel_ctx->in->message_header;
260
261 /* Let SCMI handle the message */
262 status =
263 channel_ctx->scmi_api->signal_message(channel_ctx->scmi_service_id);
264 if (status != FWK_SUCCESS) {
265 return FWK_E_HANDLER;
266 }
267
268 return FWK_SUCCESS;
269 }
270
smt_completer_handler(struct smt_channel_ctx * channel_ctx)271 static int smt_completer_handler(struct smt_channel_ctx *channel_ctx)
272 {
273 /* Commit to sending a response */
274 channel_ctx->locked = true;
275
276 return FWK_SUCCESS;
277 }
278
msg_signal_message(fwk_id_t channel_id,void * msg_in,size_t in_len,void * msg_out,size_t out_len)279 static int msg_signal_message(fwk_id_t channel_id, void *msg_in, size_t in_len, void *msg_out, size_t out_len)
280 {
281 struct smt_channel_ctx *channel_ctx;
282
283 channel_ctx =
284 &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(channel_id)];
285
286 if (!channel_ctx->msg_smt_mailbox_ready) {
287 /* Discard any message in the mailbox when not ready */
288 FWK_LOG_ERR("[MSG_SMT] Message not valid");
289
290 return FWK_SUCCESS;
291 }
292
293 /* Check if we are already processing */
294 if (channel_ctx->locked) {
295 return FWK_E_STATE;
296 }
297
298 /* Save input/output buffers */
299 channel_ctx->in = (struct mod_msg_smt_memory *)msg_in ;
300 channel_ctx->in_len = in_len;
301 channel_ctx->out = (struct mod_msg_smt_memory *)msg_out;
302 channel_ctx->out_len = out_len;
303
304 switch (channel_ctx->config->type) {
305 case MOD_MSG_SMT_CHANNEL_TYPE_REQUESTER:
306 return smt_requester_handler(channel_ctx);
307 case MOD_MSG_SMT_CHANNEL_TYPE_COMPLETER:
308 return smt_completer_handler(channel_ctx);
309 default:
310 /* Invalid config */
311 fwk_assert(false);
312 break;
313 }
314
315 return FWK_SUCCESS;
316 }
317
318 static const struct mod_msg_smt_driver_input_api driver_input_api = {
319 .signal_message = msg_signal_message,
320 };
321
322 /*
323 * Framework API
324 */
msg_init(fwk_id_t module_id,unsigned int element_count,const void * data)325 static int msg_init(fwk_id_t module_id, unsigned int element_count,
326 const void *data)
327 {
328
329 if (element_count == 0) {
330 return FWK_E_PARAM;
331 }
332
333 smt_ctx.channel_ctx_table = fwk_mm_calloc(element_count,
334 sizeof(*smt_ctx.channel_ctx_table));
335 if (smt_ctx.channel_ctx_table == NULL) {
336 return FWK_E_NOMEM;
337 }
338
339 smt_ctx.channel_count = element_count;
340
341 return FWK_SUCCESS;
342 }
343
msg_channel_init(fwk_id_t channel_id,unsigned int slot_count,const void * data)344 static int msg_channel_init(fwk_id_t channel_id, unsigned int slot_count,
345 const void *data)
346 {
347 size_t elt_idx = fwk_id_get_element_idx(channel_id);
348 struct smt_channel_ctx *channel_ctx = &smt_ctx.channel_ctx_table[elt_idx];
349
350 channel_ctx->config = (struct mod_msg_smt_channel_config*)data;
351
352 /* Validate channel config */
353 if (channel_ctx->config->type >= MOD_MSG_SMT_CHANNEL_TYPE_COUNT) {
354 fwk_assert(false);
355 return FWK_E_DATA;
356 }
357
358 channel_ctx->in = NULL;
359 channel_ctx->in_len = 0;
360 channel_ctx->out = NULL;
361 channel_ctx->out_len = 0;
362
363 channel_ctx->max_payload_size = channel_ctx->config->mailbox_size -
364 sizeof(struct mod_msg_smt_memory);
365
366 channel_ctx->msg_smt_mailbox_ready = true;
367
368 return FWK_SUCCESS;
369 }
370
msg_bind(fwk_id_t id,unsigned int round)371 static int msg_bind(fwk_id_t id, unsigned int round)
372 {
373 int status;
374 struct smt_channel_ctx *channel_ctx;
375
376 if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
377 return FWK_SUCCESS;
378 }
379
380 channel_ctx = &smt_ctx.channel_ctx_table[fwk_id_get_element_idx(id)];
381
382 if (round == 0) {
383
384 status = fwk_module_bind(channel_ctx->config->driver_id,
385 channel_ctx->config->driver_api_id,
386 &channel_ctx->driver_api);
387 if (status != FWK_SUCCESS) {
388 return status;
389 }
390
391 channel_ctx->driver_id = channel_ctx->config->driver_id;
392
393 } else if (round == 1) {
394
395 status = fwk_module_bind(channel_ctx->scmi_service_id,
396 FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_TRANSPORT),
397 &channel_ctx->scmi_api);
398 if (status != FWK_SUCCESS) {
399 return status;
400 }
401 }
402
403 return FWK_SUCCESS;
404 }
405
msg_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)406 static int msg_process_bind_request(fwk_id_t source_id,
407 fwk_id_t target_id,
408 fwk_id_t api_id,
409 const void **api)
410 {
411 struct smt_channel_ctx *channel_ctx = NULL;
412 size_t elt_idx;
413
414 /* Only bind to a channel (not the whole module) */
415 if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) {
416 return FWK_E_ACCESS;
417 }
418
419 elt_idx = fwk_id_get_element_idx(target_id);
420 if (elt_idx >= smt_ctx.channel_count) {
421 return FWK_E_ACCESS;
422 }
423
424 channel_ctx = &smt_ctx.channel_ctx_table[elt_idx];
425
426 switch (fwk_id_get_api_idx(api_id)) {
427 case MOD_MSG_SMT_API_IDX_DRIVER_INPUT:
428 /* Driver input API */
429
430 /*
431 * Make sure that the element that is trying to bind to us is the
432 * same element that we previously bound to.
433 *
434 * NOTE: We bound to an element but a sub-element should be binding
435 * back to us. This means we cannot use fwk_id_is_equal() because
436 * the ids have different types. For now we compare the indicies
437 * manually.
438 */
439 if (fwk_id_get_module_idx(channel_ctx->driver_id) ==
440 fwk_id_get_module_idx(source_id) &&
441 fwk_id_get_element_idx(channel_ctx->driver_id) ==
442 fwk_id_get_element_idx(source_id)) {
443 *api = &driver_input_api;
444 } else {
445 /* A module that we did not bind to is trying to bind to us */
446 fwk_assert(false);
447 return FWK_E_ACCESS;
448 }
449 break;
450
451 case MOD_MSG_SMT_API_IDX_SCMI_TRANSPORT:
452 /* SCMI transport API */
453 *api = &smt_mod_scmi_to_transport_api;
454 channel_ctx->scmi_service_id = source_id;
455 break;
456
457 default:
458 /* Invalid API */
459 fwk_assert(false);
460 return FWK_E_PARAM;
461 }
462
463 return FWK_SUCCESS;
464 }
465
msg_start(fwk_id_t id)466 static int msg_start(fwk_id_t id)
467 {
468 return FWK_SUCCESS;
469 }
470
471 const struct fwk_module module_msg_smt = {
472 .type = FWK_MODULE_TYPE_SERVICE,
473 .api_count = MOD_MSG_SMT_API_IDX_COUNT,
474 .init = msg_init,
475 .element_init = msg_channel_init,
476 .bind = msg_bind,
477 .start = msg_start,
478 .process_bind_request = msg_process_bind_request,
479 };
480