1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *     System Control and Management Interface (SCMI) support.
9  */
10 
11 #include <internal/mod_scmi.h>
12 #ifdef BUILD_HAS_BASE_PROTOCOL
13 #    include <internal/mod_scmi_base.h>
14 #endif
15 #include <internal/scmi.h>
16 
17 #include <mod_scmi.h>
18 #include <mod_scmi_header.h>
19 
20 #include <fwk_assert.h>
21 #include <fwk_core.h>
22 #include <fwk_event.h>
23 #include <fwk_id.h>
24 #include <fwk_log.h>
25 #include <fwk_macros.h>
26 #include <fwk_mm.h>
27 #include <fwk_module.h>
28 #include <fwk_module_idx.h>
29 #include <fwk_notification.h>
30 #include <fwk_status.h>
31 #include <fwk_string.h>
32 
33 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
34 #    include <mod_resource_perms.h>
35 #endif
36 
37 #include <inttypes.h>
38 
39 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
40 
41 /* Following macros are used for scmi notification related operations */
42 #    define MOD_SCMI_PROTOCOL_MAX_OPERATION_ID 0x20
43 #    define MOD_SCMI_PROTOCOL_OPERATION_IDX_INVALID 0xFF
44 
45 struct scmi_notification_subscribers {
46     unsigned int agent_count;
47     unsigned int element_count;
48     unsigned int operation_count;
49     unsigned int operation_idx;
50     uint8_t operation_id_to_idx[MOD_SCMI_PROTOCOL_MAX_OPERATION_ID];
51 
52     /*
53      * Table of protocol operations for which SCMI notification is requested
54      * An agent which is requesting a SCMI notification is identified using its
55      * service_id and associated context as below.
56      *
57      * Usually, a notification is requested for
58      * 1. A specific operation on the protocol.
59      * 2. An element (e.g. domain_id)
60      * 3. And a specific agent (e.g PSCI/OSPM)
61      *
62      * e.g. in Performance domain case,
63      * 1. operation_id maps to
64      *    either PERFORMANCE_LIMITS_CHANGED/PERFORMANCE_LEVEL_CHANGED
65      * 2. element maps to a performance domain.
66      * 3. And an agent maps to either a PSCI agent or an OSPM agent
67      *
68      * Thus, a service_id of a requesting agent can be indexed using above
69      * information using a simple 3-dimentional array as below
70      *
71      *   agent_service_ids[operation_idx][element_idx][agent_idx]
72      */
73     fwk_id_t *agent_service_ids;
74 };
75 
76 #endif
77 
78 /*
79  * Entry zero (0) of the protocol table 'protocol_table' is not used, as index
80  * 0 is the index of the unused entries of the 'scmi_protocol_id_to_idx[]'
81  * table. Entry one (1) is reserved for the base protocol implemented in this
82  * file.
83  */
84 #define PROTOCOL_TABLE_BASE_PROTOCOL_IDX 1
85 #define PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT 2
86 
87 static struct mod_scmi_ctx scmi_ctx;
88 
89 /*
90  * Utility functions
91  */
92 
93 /*
94  * Return a packed 32-bit message header comprised of an 8-bit message
95  * identifier, a 2-bit message type, an 8-bit protocol identifier,
96  * and a 10-bit token.
97  */
scmi_message_header(uint8_t message_id,uint8_t message_type,uint8_t protocol_id,uint8_t token)98 static uint32_t scmi_message_header(uint8_t message_id,
99     uint8_t message_type, uint8_t protocol_id, uint8_t token)
100 {
101     return (
102         (((message_id) << SCMI_MESSAGE_HEADER_MESSAGE_ID_POS) &
103          SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK) |
104         (((message_type) << SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS) &
105          SCMI_MESSAGE_HEADER_MESSAGE_TYPE_MASK) |
106         (((protocol_id) << SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS) &
107          SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK) |
108         (((token) << SCMI_MESSAGE_HEADER_TOKEN_POS) &
109          SCMI_MESSAGE_HEADER_TOKEN_MASK));
110 }
111 
read_message_id(uint32_t message_header)112 static uint16_t read_message_id(uint32_t message_header)
113 {
114     return (uint16_t)(
115         ((message_header & SCMI_MESSAGE_HEADER_MESSAGE_ID_MASK) >>
116          SCMI_MESSAGE_HEADER_MESSAGE_ID_POS));
117 }
118 
read_message_type(uint32_t message_header)119 static uint8_t read_message_type(uint32_t message_header)
120 {
121     return (uint8_t)(
122         ((message_header & SCMI_MESSAGE_HEADER_MESSAGE_TYPE_MASK) >>
123          SCMI_MESSAGE_HEADER_MESSAGE_TYPE_POS));
124 }
125 
read_protocol_id(uint32_t message_header)126 static uint8_t read_protocol_id(uint32_t message_header)
127 {
128     return (uint8_t)(
129         ((message_header & SCMI_MESSAGE_HEADER_PROTOCOL_ID_MASK) >>
130          SCMI_MESSAGE_HEADER_PROTOCOL_ID_POS));
131 }
132 
read_token(uint32_t message_header)133 static uint16_t read_token(uint32_t message_header)
134 {
135     return (uint16_t)(
136         ((message_header & SCMI_MESSAGE_HEADER_TOKEN_MASK) >>
137          SCMI_MESSAGE_HEADER_TOKEN_POS));
138 }
139 
get_message_type_str(const struct scmi_service_ctx * ctx)140 static const char *get_message_type_str(const struct scmi_service_ctx *ctx)
141 {
142     enum mod_scmi_message_type message_type = ctx->scmi_message_type;
143     switch (message_type) {
144     case MOD_SCMI_MESSAGE_TYPE_COMMAND:
145         if (ctx->config->scmi_entity_role == MOD_SCMI_ROLE_PLATFORM) {
146             return "Cmd";
147         } else if (ctx->config->scmi_entity_role == MOD_SCMI_ROLE_AGENT) {
148             return "Cmd-Resp";
149         } else {
150             return "";
151         }
152 
153     case MOD_SCMI_MESSAGE_TYPE_DELAYED_RESPONSE:
154         return "Del-Resp";
155 
156     case MOD_SCMI_MESSAGE_TYPE_NOTIFICATION:
157         return "Notif";
158 
159     default:
160         return "Invalid message";
161     }
162 }
163 
164 /*
165  * Transport entity -> SCMI module
166  */
signal_error(fwk_id_t service_id)167 static int signal_error(fwk_id_t service_id)
168 {
169     fwk_id_t transport_id;
170     struct scmi_service_ctx *ctx;
171 
172     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
173     transport_id = ctx->transport_id;
174 
175     return ctx->respond(transport_id, &(int32_t){SCMI_PROTOCOL_ERROR},
176                         sizeof(int32_t));
177 }
178 
signal_message(fwk_id_t service_id)179 static int signal_message(fwk_id_t service_id)
180 {
181     struct fwk_event_light event = (struct fwk_event_light){
182         .id = FWK_ID_EVENT(FWK_MODULE_IDX_SCMI, 0),
183         .source_id = FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
184         .target_id = service_id,
185     };
186 
187     return fwk_put_event(&event);
188 }
189 
190 static const struct mod_scmi_from_transport_api scmi_from_transport_api = {
191     .signal_error = signal_error,
192     .signal_message = signal_message,
193 };
194 
195 /*
196  * SCMI protocol module -> SCMI module interface
197  */
198 
get_agent_count(unsigned int * agent_count)199 static int get_agent_count(unsigned int *agent_count)
200 {
201     if (agent_count == NULL) {
202         return FWK_E_PARAM;
203     }
204 
205     /* Include the platform in the count */
206     *agent_count = scmi_ctx.config->agent_count + 1u;
207 
208     return FWK_SUCCESS;
209 }
210 
211 
get_agent_id(fwk_id_t service_id,unsigned int * agent_id)212 static int get_agent_id(fwk_id_t service_id, unsigned int *agent_id)
213 {
214     struct scmi_service_ctx *ctx;
215 
216     if (agent_id == NULL) {
217         return FWK_E_PARAM;
218     }
219 
220     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
221 
222     *agent_id = ctx->config->scmi_agent_id;
223 
224     return FWK_SUCCESS;
225 }
226 
get_agent_type(uint32_t scmi_agent_id,enum scmi_agent_type * agent_type)227 static int get_agent_type(uint32_t scmi_agent_id,
228                           enum scmi_agent_type *agent_type)
229 {
230     if ((agent_type == NULL) ||
231         (scmi_agent_id > scmi_ctx.config->agent_count) ||
232         (scmi_agent_id == MOD_SCMI_PLATFORM_ID)) {
233         return FWK_E_PARAM;
234     }
235 
236     *agent_type = scmi_ctx.config->agent_table[scmi_agent_id].type;
237 
238     return FWK_SUCCESS;
239 }
240 
get_max_payload_size(fwk_id_t service_id,size_t * size)241 static int get_max_payload_size(fwk_id_t service_id, size_t *size)
242 {
243     struct scmi_service_ctx *ctx;
244 
245     if (size == NULL) {
246         return FWK_E_PARAM;
247     }
248 
249     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
250 
251     return ctx->transport_api->get_max_payload_size(ctx->transport_id, size);
252 }
253 
write_payload(fwk_id_t service_id,size_t offset,const void * payload,size_t size)254 static int write_payload(fwk_id_t service_id, size_t offset,
255                          const void *payload, size_t size)
256 {
257     const struct scmi_service_ctx *ctx;
258 
259     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
260 
261     return ctx->transport_api->write_payload(ctx->transport_id,
262                                              offset, payload, size);
263 }
264 
respond(fwk_id_t service_id,const void * payload,size_t size)265 static int respond(fwk_id_t service_id, const void *payload, size_t size)
266 {
267     int status;
268     const struct scmi_service_ctx *ctx;
269     const char *service_name;
270     const char *message_type_name;
271 
272     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
273 
274     service_name = fwk_module_get_element_name(service_id);
275     message_type_name = get_message_type_str(ctx);
276 
277     /*
278      * Print to the error log if the message was not successfully processed.
279      * We assume here that the first payload entry of the command response
280      * holds an SCMI status code. This is the case for all the SCMI commands
281      * specified so far, but it is not explicitly stated (yet) in the
282      * specification it should be like that for all commands.
283      */
284     if ((payload != NULL) && (*((int32_t *)payload) < SCMI_SUCCESS)) {
285 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
286         FWK_LOG_ERR(
287             "[SCMI] %s: %s [%" PRIu16 " (0x%x:0x%x)] returned error (%d)",
288             service_name,
289             message_type_name,
290             ctx->scmi_token,
291             ctx->scmi_protocol_id,
292             ctx->scmi_message_id,
293             *((int *)payload));
294 #else
295         (void)service_name;
296         (void)message_type_name;
297 #endif
298     } else {
299 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_DEBUG
300         FWK_LOG_DEBUG(
301             "[SCMI] %s: %s [%" PRIu16 " (0x%x:0x%x)] returned successfully",
302             service_name,
303             message_type_name,
304             ctx->scmi_token,
305             ctx->scmi_protocol_id,
306             ctx->scmi_message_id);
307 #endif
308     }
309 
310     status = ctx->respond(ctx->transport_id, payload, size);
311     if (status != FWK_SUCCESS) {
312 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
313         FWK_LOG_ERR(
314             "[SCMI] %s: %s [%" PRIu16
315             " (0x%x:0x%x)] failed to respond (%s)",
316             service_name,
317             message_type_name,
318             ctx->scmi_token,
319             ctx->scmi_protocol_id,
320             ctx->scmi_message_id,
321             fwk_status_str(status));
322 #endif
323     }
324     return status;
325 }
326 
scmi_notify(fwk_id_t id,int protocol_id,int message_id,const void * payload,size_t size)327 static void scmi_notify(fwk_id_t id, int protocol_id, int message_id,
328     const void *payload, size_t size)
329 {
330     const struct scmi_service_ctx *ctx, *p2a_ctx;
331     uint32_t message_header;
332     int status;
333     bool request_ack_by_interrupt;
334 
335     /*
336      * The ID is the identifier of the service channel which
337      * the agent used to request notificatiosn on. This ID is
338      * linked to a P2A channel by the scmi_p2a_id.
339      */
340     if (fwk_id_is_equal(id, FWK_ID_NONE)) {
341         return;
342     }
343 
344     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(id)];
345     if (ctx == NULL) {
346         return;
347     }
348     /* ctx is the original A2P service channel */
349     if (fwk_id_is_equal(ctx->config->scmi_p2a_id, FWK_ID_NONE)) {
350         return;
351     }
352     /* Get the P2A service channel for A2P ctx */
353     p2a_ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(
354         ctx->config->scmi_p2a_id)];
355     if ((p2a_ctx == NULL) || (p2a_ctx->transmit == NULL)) {
356         return; /* No notification service configured */
357     }
358 
359     message_header = scmi_message_header(
360         (uint8_t)message_id,
361         (uint8_t)MOD_SCMI_MESSAGE_TYPE_NOTIFICATION,
362         (uint8_t)protocol_id,
363         0);
364 
365     request_ack_by_interrupt = false;
366     status = p2a_ctx->transmit(
367         p2a_ctx->transport_id,
368         message_header,
369         payload,
370         size,
371         request_ack_by_interrupt);
372     if (status != FWK_SUCCESS) {
373         FWK_LOG_DEBUG("[SCMI] %s @%d", __func__, __LINE__);
374     }
375 }
376 
scmi_send_message(uint8_t message_id,uint8_t protocol_id,uint8_t token,fwk_id_t service_id,const void * payload,size_t payload_size,bool request_ack_by_interrupt)377 int scmi_send_message(
378     uint8_t message_id,
379     uint8_t protocol_id,
380     uint8_t token,
381     fwk_id_t service_id,
382     const void *payload,
383     size_t payload_size,
384     bool request_ack_by_interrupt)
385 {
386     int status;
387 
388     /* All commands, synchronous or asynchronous, have a message type of 0 */
389     uint8_t message_type = 0;
390 
391     /* Create the SCMI message header from the arguments */
392     uint32_t message_header =
393         scmi_message_header(message_id, message_type, protocol_id, token);
394 
395     if (fwk_id_is_equal(service_id, FWK_ID_NONE)) {
396         return FWK_E_DATA;
397     }
398 
399     /* Fetch scmi module context data using the service_id */
400     const struct mod_scmi_to_transport_api *transport_api;
401     const struct scmi_service_ctx *ctx;
402     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
403 
404     if (ctx == NULL) {
405         return FWK_E_DATA;
406     }
407 
408     /* Initalize the transport api pointer to TRANSPORT module api */
409     transport_api = ctx->transport_api;
410 
411     /* Send the SCMI message using the transport_transmit() API  */
412     status = transport_api->transmit(
413         ctx->transport_id,
414         message_header,
415         payload,
416         payload_size,
417         request_ack_by_interrupt);
418 
419 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
420     if (status == FWK_SUCCESS) {
421         FWK_LOG_DEBUG(
422             "[SCMI] %s: Cmd [%" PRIu16 " (0x%x:0x%x)] was sent",
423             fwk_module_get_element_name(service_id),
424             token,
425             protocol_id,
426             message_id);
427     } else {
428         FWK_LOG_ERR(
429             "[SCMI] %s: Cmd [%" PRIu16 " (0x%x:0x%x)] failed to respond (%s)",
430             fwk_module_get_element_name(service_id),
431             token,
432             protocol_id,
433             message_id,
434             fwk_status_str(status));
435     }
436 #endif
437 
438     return status;
439 };
440 
response_message_handler(fwk_id_t service_id)441 int response_message_handler(fwk_id_t service_id)
442 {
443     int status;
444     const struct scmi_service_ctx *ctx;
445 
446     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
447 
448     /* release the tranport channel lock */
449     status =
450         ctx->transport_api->release_transport_channel_lock(ctx->transport_id);
451 
452     return status;
453 }
454 
455 static const struct mod_scmi_from_protocol_api scmi_from_protocol_api = {
456     .get_agent_count = get_agent_count,
457     .get_agent_id = get_agent_id,
458     .get_agent_type = get_agent_type,
459     .get_max_payload_size = get_max_payload_size,
460     .write_payload = write_payload,
461     .respond = respond,
462     .notify = scmi_notify,
463 };
464 
465 static const struct mod_scmi_from_protocol_req_api
466     scmi_from_protocol_req_api = {
467         .scmi_send_message = scmi_send_message,
468         .response_message_handler = response_message_handler,
469     };
470 
471 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
notification_subscribers(unsigned int protocol_id)472 static struct scmi_notification_subscribers *notification_subscribers(
473     unsigned int protocol_id)
474 {
475     unsigned int protocol_idx;
476     protocol_idx = scmi_ctx.scmi_protocol_id_to_idx[protocol_id];
477     /*
478      * Couple of entries are reserved in scmi_protocol_id_to_idx,
479      * out of these, one is reserved for the Base protocol. Since we may need
480      * to enable notification for the Base protocol adjust the protocol_idx
481      * accordingly.
482      */
483     protocol_idx -= (PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT - 1);
484     return &scmi_ctx.scmi_notif_subscribers[protocol_idx];
485 }
486 
scmi_notification_init(unsigned int protocol_id,unsigned int agent_count,unsigned int element_count,unsigned int operation_count)487 static int scmi_notification_init(
488     unsigned int protocol_id,
489     unsigned int agent_count,
490     unsigned int element_count,
491     unsigned int operation_count)
492 {
493     int i;
494     int total_count;
495     struct scmi_notification_subscribers *subscribers =
496         notification_subscribers(protocol_id);
497 
498     subscribers->agent_count = agent_count;
499     subscribers->element_count = element_count;
500     subscribers->operation_count = operation_count;
501 
502     total_count = (int)(operation_count * element_count * agent_count);
503 
504     subscribers->agent_service_ids =
505         fwk_mm_calloc((size_t)total_count, sizeof(fwk_id_t));
506 
507     /*
508      * Mark all operations_idx as invalid. This will be updated
509      * whenever an agent subscribes to a notification for an operation.
510      */
511     fwk_str_memset(
512         subscribers->operation_id_to_idx,
513         MOD_SCMI_PROTOCOL_OPERATION_IDX_INVALID,
514         MOD_SCMI_PROTOCOL_MAX_OPERATION_ID);
515 
516     for (i = 0; i < total_count; i++) {
517         subscribers->agent_service_ids[i] = FWK_ID_NONE;
518     }
519 
520     return FWK_SUCCESS;
521 }
522 
scmi_notification_service_idx(unsigned int agent_idx,unsigned int element_idx,unsigned int operation_idx,unsigned int agent_count,unsigned int element_count)523 static int scmi_notification_service_idx(
524     unsigned int agent_idx,
525     unsigned int element_idx,
526     unsigned int operation_idx,
527     unsigned int agent_count,
528     unsigned int element_count)
529 {
530     /*
531      * The index is from a 3-dimentional array
532      * [operation_idx][element_idx][agent_idx]
533      * The calculation of service_id offset in above array is as below
534      * 1. Get the offset of the service_id in the 3rd array .
535      *    +
536      * 2. Get the offset of the 3rd array within the 2nd array.
537      *    +
538      * 3. Get the offset of the 2nd array within the first array.
539      */
540     return (
541         int)(agent_idx + element_idx * agent_count + operation_idx * element_count * agent_count);
542 }
543 
scmi_notification_add_subscriber(unsigned int protocol_id,unsigned int element_idx,unsigned int operation_id,fwk_id_t service_id)544 static int scmi_notification_add_subscriber(
545     unsigned int protocol_id,
546     unsigned int element_idx,
547     unsigned int operation_id,
548     fwk_id_t service_id)
549 {
550     int status;
551     unsigned int service_id_idx;
552     unsigned int agent_idx;
553 
554     struct scmi_notification_subscribers *subscribers =
555         notification_subscribers(protocol_id);
556 
557     status = get_agent_id(service_id, &agent_idx);
558     if (status != FWK_SUCCESS) {
559         return status;
560     }
561 
562     fwk_assert(operation_id < MOD_SCMI_PROTOCOL_MAX_OPERATION_ID);
563     /*
564      * Initialize only if the entry is
565      * invalid (MOD_SCMI_PROTOCOL_OPERATION_IDX_INVALID)
566      */
567     if (subscribers->operation_id_to_idx[operation_id] ==
568         MOD_SCMI_PROTOCOL_OPERATION_IDX_INVALID) {
569         fwk_assert(subscribers->operation_idx < subscribers->operation_count);
570         subscribers->operation_id_to_idx[operation_id] =
571             (uint8_t)subscribers->operation_idx++;
572     }
573 
574     service_id_idx = (unsigned int)scmi_notification_service_idx(
575         agent_idx,
576         element_idx,
577         subscribers->operation_id_to_idx[operation_id],
578         subscribers->agent_count,
579         subscribers->element_count);
580 
581     subscribers->agent_service_ids[service_id_idx] = service_id;
582 
583     return FWK_SUCCESS;
584 }
585 
scmi_notification_remove_subscriber(unsigned int protocol_id,unsigned int agent_idx,unsigned int element_idx,unsigned int operation_id)586 static int scmi_notification_remove_subscriber(
587     unsigned int protocol_id,
588     unsigned int agent_idx,
589     unsigned int element_idx,
590     unsigned int operation_id)
591 {
592     unsigned int operation_idx = 0;
593     unsigned int service_id_idx;
594 
595     struct scmi_notification_subscribers *subscribers =
596         notification_subscribers(protocol_id);
597 
598     fwk_assert(operation_id < MOD_SCMI_PROTOCOL_MAX_OPERATION_ID);
599 
600     operation_idx = subscribers->operation_id_to_idx[operation_id];
601 
602     service_id_idx = (unsigned int)scmi_notification_service_idx(
603         agent_idx,
604         element_idx,
605         operation_idx,
606         subscribers->agent_count,
607         subscribers->element_count);
608 
609     subscribers->agent_service_ids[service_id_idx] = FWK_ID_NONE;
610 
611     return FWK_SUCCESS;
612 }
613 
scmi_notification_notify(unsigned int protocol_id,unsigned int operation_id,unsigned int scmi_response_id,void * payload_p2a,size_t payload_size)614 static int scmi_notification_notify(
615     unsigned int protocol_id,
616     unsigned int operation_id,
617     unsigned int scmi_response_id,
618     void *payload_p2a,
619     size_t payload_size)
620 {
621     unsigned int i, j;
622     unsigned int operation_idx;
623     fwk_id_t service_id;
624     unsigned int service_id_idx;
625 
626     struct scmi_notification_subscribers *subscribers =
627         notification_subscribers(protocol_id);
628 
629     fwk_assert(operation_id < MOD_SCMI_PROTOCOL_MAX_OPERATION_ID);
630     operation_idx = subscribers->operation_id_to_idx[operation_id];
631 
632     /*
633      * Silently return FWK_SUCCESS if no valid operation_idx is found
634      * for a given operation_id. This is required in where(e.g. DVFS/perf)
635      * scmi_notification_notify gets called before a call to
636      * scmi_notification_add_subscriber.
637      */
638     if (operation_idx == MOD_SCMI_PROTOCOL_OPERATION_IDX_INVALID) {
639         return FWK_SUCCESS;
640     }
641 
642     for (i = 0; i < subscribers->element_count; i++) {
643         /* Skip agent 0, platform agent */
644         for (j = 1; j < subscribers->agent_count; j++) {
645             service_id_idx = (unsigned int)scmi_notification_service_idx(
646                 j,
647                 i,
648                 operation_idx,
649                 subscribers->agent_count,
650                 subscribers->element_count);
651 
652             service_id = subscribers->agent_service_ids[service_id_idx];
653 
654             if (!fwk_id_is_equal(service_id, FWK_ID_NONE)) {
655                 scmi_notify(
656                     service_id,
657                     (int)protocol_id,
658                     (int)scmi_response_id,
659                     payload_p2a,
660                     payload_size);
661             }
662         }
663     }
664 
665     return FWK_SUCCESS;
666 }
667 
668 static struct mod_scmi_notification_api mod_scmi_notif_api = {
669     .scmi_notification_init = scmi_notification_init,
670     .scmi_notification_add_subscriber = scmi_notification_add_subscriber,
671     .scmi_notification_remove_subscriber = scmi_notification_remove_subscriber,
672     .scmi_notification_notify = scmi_notification_notify,
673 };
674 #endif
675 
676 /*
677  * Framework handlers
678  */
679 
scmi_init(fwk_id_t module_id,unsigned int service_count,const void * data)680 static int scmi_init(fwk_id_t module_id, unsigned int service_count,
681                      const void *data)
682 {
683     struct mod_scmi_config *config = (struct mod_scmi_config *)data;
684     unsigned int agent_idx;
685     const struct mod_scmi_agent *agent;
686 
687     if (config == NULL) {
688         return FWK_E_PARAM;
689     }
690 
691     if (config->agent_count > MOD_SCMI_AGENT_ID_MAX) {
692         return FWK_E_PARAM;
693     }
694 
695     /*
696      * Loop over the agent descriptors. The MOD_SCMI_PLATFORM_ID(0) entry of
697      * the table - that would refer to the platform - is ignored.
698      */
699     for (agent_idx = MOD_SCMI_PLATFORM_ID + 1;
700          agent_idx <= config->agent_count;
701          agent_idx++) {
702         agent = &config->agent_table[agent_idx];
703         if (agent->type >= SCMI_AGENT_TYPE_COUNT) {
704             return FWK_E_PARAM;
705         }
706     }
707 
708     scmi_ctx.protocol_table = fwk_mm_calloc(
709         config->protocol_count_max + PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT,
710         sizeof(scmi_ctx.protocol_table[0]));
711 
712     if (config->protocol_requester_count_max != 0) {
713         scmi_ctx.protocol_requester_table = fwk_mm_calloc(
714             config->protocol_requester_count_max,
715             sizeof(scmi_ctx.protocol_requester_table[0]));
716     } else {
717         scmi_ctx.protocol_requester_table = NULL;
718     }
719 
720     scmi_ctx.service_ctx_table = fwk_mm_calloc(
721         service_count, sizeof(scmi_ctx.service_ctx_table[0]));
722 
723 #ifdef BUILD_HAS_BASE_PROTOCOL
724     scmi_ctx.protocol_table[PROTOCOL_TABLE_BASE_PROTOCOL_IDX].message_handler =
725         scmi_base_message_handler;
726     scmi_ctx.scmi_protocol_id_to_idx[MOD_SCMI_PROTOCOL_ID_BASE] =
727         PROTOCOL_TABLE_BASE_PROTOCOL_IDX;
728     scmi_base_set_api(&scmi_from_protocol_api);
729     scmi_base_set_shared_ctx(&scmi_ctx);
730 #endif
731     scmi_ctx.config = config;
732 
733     return FWK_SUCCESS;
734 }
735 
scmi_service_init(fwk_id_t service_id,unsigned int unused,const void * data)736 static int scmi_service_init(fwk_id_t service_id, unsigned int unused,
737                              const void *data)
738 {
739     const struct mod_scmi_service_config *config =
740         (struct mod_scmi_service_config *)data;
741     struct scmi_service_ctx *ctx;
742 
743     if (((config->scmi_agent_id == MOD_SCMI_PLATFORM_ID) ||
744          (config->scmi_agent_id > scmi_ctx.config->agent_count)) &&
745         (config->scmi_entity_role == MOD_SCMI_ROLE_PLATFORM)) {
746         return FWK_E_PARAM;
747     }
748 
749     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(service_id)];
750     ctx->config = config;
751 
752     return FWK_SUCCESS;
753 }
754 
scmi_bind(fwk_id_t id,unsigned int round)755 static int scmi_bind(fwk_id_t id, unsigned int round)
756 {
757     int status;
758     struct scmi_service_ctx *ctx;
759     const struct mod_scmi_to_transport_api *transport_api = NULL;
760     unsigned int protocol_idx;
761     struct scmi_protocol *protocol, *protocol_req;
762     struct mod_scmi_to_protocol_api *protocol_api = NULL;
763     uint8_t scmi_protocol_id;
764 
765     if (round == 0) {
766         if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
767             return FWK_SUCCESS;
768         }
769 
770         ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(id)];
771         status = fwk_module_bind(ctx->config->transport_id,
772                                  ctx->config->transport_api_id, &transport_api);
773         if (status != FWK_SUCCESS) {
774             return status;
775         }
776 
777         if ((transport_api->get_secure == NULL) ||
778             (transport_api->get_max_payload_size == NULL) ||
779             (transport_api->get_message_header == NULL) ||
780             (transport_api->get_payload == NULL) ||
781             (transport_api->write_payload == NULL)) {
782             return FWK_E_DATA;
783         }
784 
785         ctx->transport_api = transport_api;
786         ctx->transport_id = ctx->config->transport_id;
787         ctx->respond = transport_api->respond;
788         ctx->transmit = transport_api->transmit;
789 
790         return FWK_SUCCESS;
791     }
792 
793     if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
794         return FWK_SUCCESS;
795     }
796 
797     for (protocol_idx = 0;
798          protocol_idx < scmi_ctx.protocol_count; protocol_idx++) {
799         protocol = &scmi_ctx.protocol_table[
800             PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT + protocol_idx];
801 
802         status = fwk_module_bind(protocol->id,
803             FWK_ID_API(fwk_id_get_module_idx(protocol->id), 0), &protocol_api);
804         if (status != FWK_SUCCESS) {
805             return status;
806         }
807 
808         if ((protocol_api->get_scmi_protocol_id == NULL) ||
809             (protocol_api->message_handler == NULL)) {
810             return FWK_E_DATA;
811         }
812         status = protocol_api->get_scmi_protocol_id(protocol->id,
813                                                     &scmi_protocol_id);
814         if (status != FWK_SUCCESS) {
815             return status;
816         }
817 
818         if (scmi_ctx.scmi_protocol_id_to_idx[scmi_protocol_id] != 0) {
819             return FWK_E_STATE;
820         }
821 
822         scmi_ctx.scmi_protocol_id_to_idx[scmi_protocol_id] =
823             (uint8_t)(protocol_idx + PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT);
824         protocol->message_handler = protocol_api->message_handler;
825     }
826 
827     for (protocol_idx = 0; protocol_idx < scmi_ctx.protocol_requester_count;
828          protocol_idx++) {
829         protocol_req = &scmi_ctx.protocol_requester_table[protocol_idx];
830 
831         status = fwk_module_bind(
832             protocol_req->id,
833             FWK_ID_API(fwk_id_get_module_idx(protocol_req->id), 0),
834             &protocol_api);
835         if (status != FWK_SUCCESS) {
836             return status;
837         }
838 
839         if ((protocol_api->get_scmi_protocol_id == NULL) ||
840             (protocol_api->message_handler == NULL)) {
841             return FWK_E_DATA;
842         }
843         status = protocol_api->get_scmi_protocol_id(
844             protocol_req->id, &scmi_protocol_id);
845         if (status != FWK_SUCCESS) {
846             return status;
847         }
848 
849         scmi_ctx.scmi_protocol_requester_id_to_idx[scmi_protocol_id] =
850             (uint8_t)(protocol_idx);
851         protocol_req->message_handler = protocol_api->message_handler;
852     }
853 
854 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
855     status = fwk_module_bind(
856         FWK_ID_MODULE(FWK_MODULE_IDX_RESOURCE_PERMS),
857         FWK_ID_API(FWK_MODULE_IDX_RESOURCE_PERMS, MOD_RES_PERM_RESOURCE_PERMS),
858         &scmi_ctx.res_perms_api);
859     if (status != FWK_SUCCESS) {
860         return status;
861     }
862 #endif
863 
864     return FWK_SUCCESS;
865 }
866 
scmi_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)867 static int scmi_process_bind_request(fwk_id_t source_id, fwk_id_t target_id,
868                                      fwk_id_t api_id, const void **api)
869 {
870     unsigned int api_idx;
871     struct scmi_service_ctx *ctx;
872     enum mod_scmi_api_idx api_id_type;
873 
874     api_idx = fwk_id_get_api_idx(api_id);
875 
876     api_id_type = (enum mod_scmi_api_idx)api_idx;
877 
878     switch (api_id_type) {
879     case MOD_SCMI_API_IDX_PROTOCOL:
880         if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) {
881             return FWK_E_SUPPORT;
882         }
883 
884         if (scmi_ctx.protocol_count >= scmi_ctx.config->protocol_count_max) {
885             return FWK_E_NOMEM;
886         }
887 
888         scmi_ctx.protocol_table[PROTOCOL_TABLE_RESERVED_ENTRIES_COUNT +
889                                 scmi_ctx.protocol_count++].id = source_id;
890         *api = &scmi_from_protocol_api;
891         break;
892 
893     case MOD_SCMI_API_IDX_PROTOCOL_REQ:
894         if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) {
895             return FWK_E_SUPPORT;
896         }
897         if (scmi_ctx.protocol_requester_count >=
898             scmi_ctx.config->protocol_requester_count_max) {
899             return FWK_E_PARAM;
900         }
901 
902         scmi_ctx.protocol_requester_table[scmi_ctx.protocol_requester_count++]
903             .id = source_id;
904         *api = &scmi_from_protocol_req_api;
905         break;
906 
907     case MOD_SCMI_API_IDX_TRANSPORT:
908         if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) {
909             return FWK_E_SUPPORT;
910         }
911 
912         ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(target_id)];
913         if (!fwk_id_is_equal(source_id, ctx->transport_id)) {
914             return FWK_E_ACCESS;
915         }
916 
917         *api = &scmi_from_transport_api;
918         break;
919 
920 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
921     case MOD_SCMI_API_IDX_NOTIFICATION:
922         if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) {
923             return FWK_E_SUPPORT;
924         }
925 
926         *api = &mod_scmi_notif_api;
927         break;
928 #endif
929 
930     default:
931         return FWK_E_SUPPORT;
932     };
933 
934     return FWK_SUCCESS;
935 }
936 
scmi_process_event(const struct fwk_event * event,struct fwk_event * resp)937 static int scmi_process_event(const struct fwk_event *event,
938                               struct fwk_event *resp)
939 {
940     int status;
941     struct scmi_service_ctx *ctx;
942     const struct mod_scmi_to_transport_api *transport_api;
943     fwk_id_t transport_id;
944     uint32_t message_header;
945     const void *payload;
946     size_t payload_size;
947     unsigned int protocol_idx;
948 #ifndef BUILD_HAS_MOD_RESOURCE_PERMS
949     enum scmi_agent_type agent_type;
950     unsigned int agent_id;
951     unsigned int dis_protocol_list_psci_index;
952 #endif
953     struct scmi_protocol *protocol;
954     const char *service_name;
955     const char *message_type_name;
956 
957     ctx = &scmi_ctx.service_ctx_table[fwk_id_get_element_idx(event->target_id)];
958     transport_api = ctx->transport_api;
959     transport_id = ctx->transport_id;
960 
961     service_name = fwk_module_get_element_name(event->target_id);
962 
963     status = transport_api->get_message_header(transport_id, &message_header);
964     if (status != FWK_SUCCESS) {
965 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
966         FWK_LOG_ERR("[SCMI] %s: Unable to read message header", service_name);
967 #else
968         (void)service_name;
969 #endif
970         return status;
971     }
972 
973     status = transport_api->get_payload(transport_id, &payload, &payload_size);
974     if (status != FWK_SUCCESS) {
975 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
976         FWK_LOG_ERR("[SCMI] %s: Unable to read message payload", service_name);
977 #else
978         (void)service_name;
979 #endif
980         return status;
981     }
982 
983     ctx->scmi_protocol_id = read_protocol_id(message_header);
984     ctx->scmi_message_id = read_message_id(message_header);
985     ctx->scmi_message_type =
986         (enum mod_scmi_message_type)read_message_type(message_header);
987     ctx->scmi_token = read_token(message_header);
988     message_type_name = get_message_type_str(ctx);
989 
990 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_DEBUG
991     FWK_LOG_DEBUG(
992         "[SCMI] %s: %s [%" PRIu16 " (0x%x:0x%x)] was received",
993         service_name,
994         message_type_name,
995         ctx->scmi_token,
996         ctx->scmi_protocol_id,
997         ctx->scmi_message_id);
998 #else
999     (void)service_name;
1000     (void)message_type_name;
1001 #endif
1002     if (ctx->config->scmi_entity_role == MOD_SCMI_ROLE_PLATFORM) {
1003         protocol_idx = scmi_ctx.scmi_protocol_id_to_idx[ctx->scmi_protocol_id];
1004         if (protocol_idx == 0) {
1005 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
1006             FWK_LOG_ERR(
1007                 "[SCMI] %s: %s [%" PRIu16
1008                 "(0x%x:0x%x)] requested an unsupported protocol",
1009                 service_name,
1010                 message_type_name,
1011                 ctx->scmi_token,
1012                 ctx->scmi_protocol_id,
1013                 ctx->scmi_message_id);
1014 #endif
1015             status = ctx->respond(
1016                 transport_id,
1017                 &(int32_t){ SCMI_NOT_SUPPORTED },
1018                 sizeof(int32_t));
1019             if (status != FWK_SUCCESS) {
1020                 FWK_LOG_DEBUG("[SCMI] %s @%d", __func__, __LINE__);
1021             }
1022             return FWK_SUCCESS;
1023         }
1024 
1025 #ifndef BUILD_HAS_MOD_RESOURCE_PERMS
1026     status = get_agent_id(event->target_id, &agent_id);
1027     if (status != FWK_SUCCESS) {
1028 #    if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
1029         FWK_LOG_ERR("[SCMI] %s: Unable to get agent id", service_name);
1030 #    endif
1031         return status;
1032     }
1033 
1034     status = get_agent_type(agent_id, &agent_type);
1035     if (status != FWK_SUCCESS) {
1036 #    if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
1037         FWK_LOG_ERR("[SCMI] %s: Unable to get agent type", service_name);
1038 #    endif
1039         return status;
1040     }
1041 
1042     if (agent_type == SCMI_AGENT_TYPE_PSCI) {
1043         /*
1044          * check if the current protocol is within the disabled protocols
1045          * list for PSCI agent.
1046          */
1047         for (dis_protocol_list_psci_index = 0; dis_protocol_list_psci_index <
1048              scmi_ctx.config->dis_protocol_count_psci;
1049              dis_protocol_list_psci_index++) {
1050             if (ctx->scmi_protocol_id ==
1051                 scmi_ctx.config
1052                     ->dis_protocol_list_psci[dis_protocol_list_psci_index]) {
1053 #    if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
1054                 FWK_LOG_ERR(
1055                     "[SCMI] %s: %s [%" PRIu16
1056                     "(0x%x:0x%x)] requested a denied protocol",
1057                     service_name,
1058                     message_type_name,
1059                     ctx->scmi_token,
1060                     ctx->scmi_protocol_id,
1061                     ctx->scmi_message_id);
1062 #    endif
1063                 status = ctx->respond(
1064                     transport_id, &(int32_t){ SCMI_DENIED }, sizeof(int32_t));
1065                 if (status != FWK_SUCCESS) {
1066                     FWK_LOG_DEBUG("[SCMI] %s @%d", __func__, __LINE__);
1067                 }
1068                 return FWK_SUCCESS;
1069             }
1070         }
1071     }
1072 #endif
1073     protocol = &scmi_ctx.protocol_table[protocol_idx];
1074     } else if (ctx->config->scmi_entity_role == MOD_SCMI_ROLE_AGENT) {
1075         protocol_idx =
1076             scmi_ctx.scmi_protocol_requester_id_to_idx[ctx->scmi_protocol_id];
1077         protocol = &scmi_ctx.protocol_requester_table[protocol_idx];
1078     } else {
1079         return FWK_E_INIT;
1080     }
1081 
1082     status = protocol->message_handler(
1083         protocol->id,
1084         event->target_id,
1085         payload,
1086         payload_size,
1087         ctx->scmi_message_id);
1088 
1089     if (status != FWK_SUCCESS) {
1090 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
1091         FWK_LOG_ERR(
1092             "[SCMI] %s: %s [%" PRIu16 " (0x%x:0x%x)] handler error (%s)",
1093             service_name,
1094             message_type_name,
1095             ctx->scmi_token,
1096             ctx->scmi_protocol_id,
1097             ctx->scmi_message_id,
1098             fwk_status_str(status));
1099 #endif
1100         return FWK_SUCCESS;
1101     }
1102 
1103     /*
1104      * If we are receiving a command response, we need to set the transport
1105      * layer that we already received the response and are free
1106      */
1107     if (ctx->config->scmi_entity_role == MOD_SCMI_ROLE_AGENT) {
1108         status =
1109             ctx->transport_api->release_transport_channel_lock(transport_id);
1110         if (status != FWK_SUCCESS) {
1111 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
1112             FWK_LOG_ERR(
1113                 "[SCMI] %s: %s [%" PRIu16
1114                 " (0x%x:0x%x)] confirm reception error (%s)",
1115                 service_name,
1116                 message_type_name,
1117                 ctx->scmi_token,
1118                 ctx->scmi_protocol_id,
1119                 ctx->scmi_message_id,
1120                 fwk_status_str(status));
1121 #endif
1122             return FWK_SUCCESS;
1123         }
1124     }
1125 
1126     return FWK_SUCCESS;
1127 }
1128 
scmi_start(fwk_id_t id)1129 static int scmi_start(fwk_id_t id)
1130 {
1131 #ifdef BUILD_HAS_NOTIFICATION
1132     const struct mod_scmi_service_config *config;
1133     unsigned int notifications_sent;
1134 
1135     if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
1136 #    ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1137         /* scmi_ctx.protocol_count + 1 to include Base protocol */
1138         scmi_ctx.scmi_notif_subscribers = fwk_mm_calloc(
1139             scmi_ctx.protocol_count + 1,
1140             sizeof(struct scmi_notification_subscribers));
1141 #    endif
1142 
1143         return FWK_SUCCESS;
1144     }
1145 
1146     config = fwk_module_get_data(id);
1147 
1148     if (fwk_id_is_equal(config->transport_notification_init_id, FWK_ID_NONE)) {
1149         /* Notify that the service is immediately ready */
1150         struct fwk_event scmi_services_initialized_notification = {
1151             .id = mod_scmi_notification_id_initialized,
1152             .source_id = id,
1153         };
1154 
1155         return fwk_notification_notify(&scmi_services_initialized_notification,
1156             &notifications_sent);
1157     }
1158 
1159     return fwk_notification_subscribe(config->transport_notification_init_id,
1160                                       config->transport_id,
1161                                       id);
1162 #else
1163     return FWK_SUCCESS;
1164 #endif
1165 }
1166 
1167 #ifdef BUILD_HAS_NOTIFICATION
scmi_process_notification(const struct fwk_event * event,struct fwk_event * resp_event)1168 static int scmi_process_notification(const struct fwk_event *event,
1169                                      struct fwk_event *resp_event)
1170 {
1171     const struct mod_scmi_service_config *config;
1172     unsigned int notifications_sent;
1173 
1174     fwk_assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT));
1175 
1176     config = fwk_module_get_data(event->target_id);
1177     if (config == NULL) {
1178         return FWK_E_PARAM;
1179     }
1180 
1181     fwk_assert(fwk_id_is_equal(event->id,
1182                                config->transport_notification_init_id));
1183 
1184     /* Notify that this service is ready */
1185     struct fwk_event scmi_services_initialized_notification = {
1186         .id = mod_scmi_notification_id_initialized,
1187         .source_id = FWK_ID_NONE
1188     };
1189 
1190     return fwk_notification_notify(&scmi_services_initialized_notification,
1191         &notifications_sent);
1192 }
1193 #endif
1194 
1195 /* SCMI module definition */
1196 const struct fwk_module module_scmi = {
1197     .api_count = (unsigned int)MOD_SCMI_API_IDX_COUNT,
1198     .event_count = 1,
1199 #ifdef BUILD_HAS_NOTIFICATION
1200     .notification_count = (unsigned int)MOD_SCMI_NOTIFICATION_IDX_COUNT,
1201 #endif
1202     .type = FWK_MODULE_TYPE_SERVICE,
1203     .init = scmi_init,
1204     .element_init = scmi_service_init,
1205     .bind = scmi_bind,
1206     .start = scmi_start,
1207     .process_bind_request = scmi_process_bind_request,
1208     .process_event = scmi_process_event,
1209 #ifdef BUILD_HAS_NOTIFICATION
1210     .process_notification = scmi_process_notification,
1211 #endif
1212 };
1213