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  *     Framework facilities.
9  */
10 
11 #include <internal/fwk_context.h>
12 #include <internal/fwk_core.h>
13 #include <internal/fwk_delayed_resp.h>
14 #include <internal/fwk_module.h>
15 
16 #include <fwk_assert.h>
17 #include <fwk_core.h>
18 #include <fwk_event.h>
19 #include <fwk_id.h>
20 #include <fwk_interrupt.h>
21 #include <fwk_list.h>
22 #include <fwk_log.h>
23 #include <fwk_mm.h>
24 #include <fwk_module.h>
25 #include <fwk_noreturn.h>
26 #include <fwk_status.h>
27 #include <fwk_string.h>
28 
29 #include <inttypes.h>
30 #include <stdbool.h>
31 
32 static struct __fwk_ctx ctx;
33 
34 static const char err_msg_line[] = "[FWK] Error %d in %s @%d";
35 static const char err_msg_func[] = "[FWK] Error %d in %s";
36 
37 enum interrupt_states {
38     UNKNOWN_STATE = 0,
39     INTERRUPT_STATE = 1,
40     NOT_INTERRUPT_STATE = 2,
41 };
42 
43 /*
44  * Static functions
45  */
46 
47 /*
48  * Duplicate an event.
49  *
50  * \param event Pointer to the event to duplicate.
51  * \param event_type Type of the event structure as defined in
52  *     \c fwk_event_type
53  *
54  * \pre \p event must not be NULL
55  *
56  * \return The pointer to the duplicated event, NULL if the allocation to
57  *      duplicate the event failed.
58  */
duplicate_event(void * event,enum fwk_event_type event_type)59 static struct fwk_event *duplicate_event(
60     void *event,
61     enum fwk_event_type event_type)
62 {
63     struct fwk_event *allocated_event = NULL;
64     unsigned int flags;
65 
66     fwk_assert(event != NULL);
67 
68     flags = fwk_interrupt_global_disable();
69     allocated_event = FWK_LIST_GET(
70         fwk_list_pop_head(&ctx.free_event_queue), struct fwk_event, slist_node);
71     (void)fwk_interrupt_global_enable(flags);
72 
73     if (allocated_event == NULL) {
74         FWK_LOG_CRIT(err_msg_func, FWK_E_NOMEM, __func__);
75         fwk_unexpected();
76 
77         return NULL;
78     }
79     if (event_type == FWK_EVENT_TYPE_LIGHT) {
80         struct fwk_event_light *light_event = (struct fwk_event_light *)event;
81         allocated_event->id = light_event->id;
82         allocated_event->source_id = light_event->source_id;
83         allocated_event->target_id = light_event->target_id;
84         allocated_event->is_notification = false;
85         allocated_event->response_requested = light_event->response_requested;
86         allocated_event->is_delayed_response = false;
87         allocated_event->is_response = false;
88     } else {
89         *allocated_event = *((struct fwk_event *)event);
90     }
91 
92     allocated_event->slist_node = (struct fwk_slist_node){ 0 };
93 
94     return allocated_event;
95 }
96 
put_event(void * event,enum interrupt_states intr_state,enum fwk_event_type event_type)97 static int put_event(
98     void *event,
99     enum interrupt_states intr_state,
100     enum fwk_event_type event_type)
101 {
102     struct fwk_event *allocated_event;
103 
104     struct fwk_event *std_event = NULL;
105 
106     if (event_type == FWK_EVENT_TYPE_STD) {
107         std_event = (struct fwk_event *)event;
108     }
109 
110     if (std_event != NULL && std_event->is_delayed_response) {
111         allocated_event = __fwk_search_delayed_response(
112             std_event->source_id, std_event->cookie);
113         if (allocated_event == NULL) {
114             FWK_LOG_CRIT(err_msg_func, FWK_E_NOMEM, __func__);
115             return FWK_E_PARAM;
116         }
117 
118         fwk_list_remove(
119             __fwk_get_delayed_response_list(std_event->source_id),
120             &allocated_event->slist_node);
121 
122         (void)memcpy(
123             allocated_event->params,
124             std_event->params,
125             sizeof(allocated_event->params));
126 
127     } else {
128         allocated_event = duplicate_event(event, event_type);
129         if (allocated_event == NULL) {
130             return FWK_E_NOMEM;
131         }
132     }
133 
134     if (std_event != NULL) {
135         allocated_event->cookie = ctx.event_cookie_counter++;
136         std_event->cookie = allocated_event->cookie;
137     }
138 
139     if (intr_state == UNKNOWN_STATE) {
140         if (fwk_is_interrupt_context()) {
141             intr_state = INTERRUPT_STATE;
142         } else {
143             intr_state = NOT_INTERRUPT_STATE;
144         }
145     }
146     if (intr_state == NOT_INTERRUPT_STATE) {
147         fwk_list_push_tail(&ctx.event_queue, &allocated_event->slist_node);
148     } else {
149         fwk_list_push_tail(&ctx.isr_event_queue, &allocated_event->slist_node);
150     }
151 
152 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_DEBUG
153     FWK_LOG_DEBUG(
154         "[FWK] Sent %" PRIu32 ": %s @ %s -> %s",
155         std_event != NULL ? std_event->cookie : 0,
156         FWK_ID_STR(allocated_event->id),
157         FWK_ID_STR(allocated_event->source_id),
158         FWK_ID_STR(allocated_event->target_id));
159 #endif
160 
161     return FWK_SUCCESS;
162 }
163 
free_event(struct fwk_event * event)164 static void free_event(struct fwk_event *event)
165 {
166     unsigned int flags;
167 
168     flags = fwk_interrupt_global_disable();
169     fwk_list_push_tail(&ctx.free_event_queue, &event->slist_node);
170     (void)fwk_interrupt_global_enable(flags);
171 }
172 
process_next_event(void)173 static void process_next_event(void)
174 {
175     int status;
176     struct fwk_event *event, *allocated_event, async_response_event;
177     const struct fwk_module *module;
178     int (*process_event)(
179         const struct fwk_event *event, struct fwk_event *resp_event);
180 
181     ctx.current_event = event = FWK_LIST_GET(
182         fwk_list_pop_head(&ctx.event_queue), struct fwk_event, slist_node);
183 
184 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_DEBUG
185     FWK_LOG_DEBUG(
186         "[FWK] Processing %" PRIu32 ": %s @ %s -> %s\n",
187         event->cookie,
188         FWK_ID_STR(event->id),
189         FWK_ID_STR(event->source_id),
190         FWK_ID_STR(event->target_id));
191 #endif
192 
193     module = fwk_module_get_ctx(event->target_id)->desc;
194     process_event = event->is_notification ? module->process_notification :
195                                              module->process_event;
196 
197     if (event->response_requested) {
198         fwk_str_memset(&async_response_event, 0, sizeof(async_response_event));
199         async_response_event = *event;
200         async_response_event.source_id = event->target_id;
201         async_response_event.target_id = event->source_id;
202         async_response_event.is_delayed_response = false;
203 
204         status = process_event(event, &async_response_event);
205         if (status != FWK_SUCCESS) {
206             FWK_LOG_CRIT(err_msg_line, status, __func__, __LINE__);
207         }
208 
209         async_response_event.is_response = true;
210         async_response_event.response_requested = false;
211         if (!async_response_event.is_delayed_response) {
212             (void)put_event(
213                 &async_response_event, UNKNOWN_STATE, FWK_EVENT_TYPE_STD);
214         } else {
215             allocated_event =
216                 duplicate_event(&async_response_event, FWK_EVENT_TYPE_STD);
217             if (allocated_event != NULL) {
218                 fwk_list_push_tail(
219                     __fwk_get_delayed_response_list(
220                         async_response_event.source_id),
221                     &allocated_event->slist_node);
222             }
223         }
224     } else {
225         status = process_event(event, &async_response_event);
226         if ((status != FWK_SUCCESS) && (status != FWK_PENDING)) {
227             FWK_LOG_CRIT(
228                 "[FWK] Process event (%s: %s -> %s) (%d)\n",
229                 FWK_ID_STR(event->id),
230                 FWK_ID_STR(event->source_id),
231                 FWK_ID_STR(event->target_id),
232                 status);
233         }
234     }
235 
236     ctx.current_event = NULL;
237     free_event(event);
238     return;
239 }
240 
process_isr(void)241 static bool process_isr(void)
242 {
243     struct fwk_event *isr_event;
244     unsigned int flags;
245 
246     flags = fwk_interrupt_global_disable();
247     isr_event = FWK_LIST_GET(
248         fwk_list_pop_head(&ctx.isr_event_queue), struct fwk_event, slist_node);
249     (void)fwk_interrupt_global_enable(flags);
250 
251     if (isr_event == NULL) {
252         return false;
253     }
254 
255 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_DEBUG
256     FWK_LOG_DEBUG(
257         "[FWK] Pulled ISR event (%s: %s -> %s)\n",
258         FWK_ID_STR(isr_event->id),
259         FWK_ID_STR(isr_event->source_id),
260         FWK_ID_STR(isr_event->target_id));
261 #endif
262 
263     fwk_list_push_tail(&ctx.event_queue, &isr_event->slist_node);
264 
265     return true;
266 }
267 
268 /*
269  * Private interface functions
270  */
271 
__fwk_init(size_t event_count)272 int __fwk_init(size_t event_count)
273 {
274     struct fwk_event *event_table, *event;
275 
276     event_table = fwk_mm_calloc(event_count, sizeof(struct fwk_event));
277 
278     /* All the event structures are free to be used. */
279     fwk_list_init(&ctx.free_event_queue);
280     fwk_list_init(&ctx.event_queue);
281     fwk_list_init(&ctx.isr_event_queue);
282 
283     for (event = event_table; event < (event_table + event_count); event++) {
284         fwk_list_push_tail(&ctx.free_event_queue, &event->slist_node);
285     }
286 
287     ctx.initialized = true;
288 
289     return FWK_SUCCESS;
290 }
291 
fwk_process_event_queue(void)292 void fwk_process_event_queue(void)
293 {
294     for (;;) {
295         while (!fwk_list_is_empty(&ctx.event_queue)) {
296             process_next_event();
297         }
298 
299         if (!process_isr()) {
300             break;
301         }
302     }
303 }
304 
__fwk_run_main_loop(void)305 noreturn void __fwk_run_main_loop(void)
306 {
307     for (;;) {
308         fwk_process_event_queue();
309         if (fwk_log_unbuffer() == FWK_SUCCESS) {
310             fwk_arch_suspend();
311         }
312     }
313 }
314 
__fwk_get_ctx(void)315 struct __fwk_ctx *__fwk_get_ctx(void)
316 {
317     return &ctx;
318 }
319 
__fwk_get_current_event(void)320 const struct fwk_event *__fwk_get_current_event(void)
321 {
322     return ctx.current_event;
323 }
324 
325 #ifdef BUILD_HAS_NOTIFICATION
__fwk_put_notification(struct fwk_event * event)326 int __fwk_put_notification(struct fwk_event *event)
327 {
328     event->is_response = false;
329     event->is_notification = true;
330 
331     return put_event(event, UNKNOWN_STATE, FWK_EVENT_TYPE_STD);
332 }
333 #endif
334 
335 /*
336  * Public interface functions
337  */
338 
__fwk_put_event(struct fwk_event * event)339 int __fwk_put_event(struct fwk_event *event)
340 {
341     int status = FWK_E_PARAM;
342     enum interrupt_states intr_state;
343 
344 #ifdef BUILD_MODE_DEBUG
345     if (!ctx.initialized) {
346         status = FWK_E_INIT;
347         goto error;
348     }
349 
350     if (event == NULL) {
351         goto error;
352     }
353 #endif
354 
355     if (fwk_is_interrupt_context()) {
356         intr_state = INTERRUPT_STATE;
357     } else {
358         intr_state = NOT_INTERRUPT_STATE;
359     }
360 
361     if ((intr_state == NOT_INTERRUPT_STATE) && (ctx.current_event != NULL)) {
362         event->source_id = ctx.current_event->target_id;
363     } else if (
364         !fwk_id_type_is_valid(event->source_id) ||
365         !fwk_module_is_valid_entity_id(event->source_id)) {
366         status = FWK_E_PARAM;
367         goto error;
368     }
369 
370 #ifdef BUILD_MODE_DEBUG
371     status = FWK_E_PARAM;
372     if (event->is_notification) {
373         if (!fwk_module_is_valid_notification_id(event->id)) {
374             goto error;
375         }
376         if ((!event->is_response) || (event->response_requested)) {
377             goto error;
378         }
379         if (fwk_id_get_module_idx(event->target_id) !=
380             fwk_id_get_module_idx(event->id)) {
381             goto error;
382         }
383     } else {
384         if (!fwk_module_is_valid_event_id(event->id)) {
385             goto error;
386         }
387         if (event->is_response) {
388             if (fwk_id_get_module_idx(event->source_id) !=
389                 fwk_id_get_module_idx(event->id)) {
390                 goto error;
391             }
392             if (event->response_requested) {
393                 goto error;
394             }
395         } else {
396             if (fwk_id_get_module_idx(event->target_id) !=
397                 fwk_id_get_module_idx(event->id)) {
398                 goto error;
399             }
400         }
401     }
402 #endif
403 
404     return put_event(event, intr_state, FWK_EVENT_TYPE_STD);
405 
406 error:
407     FWK_LOG_CRIT(err_msg_func, status, __func__);
408     return status;
409 }
410 
__fwk_put_event_light(struct fwk_event_light * event)411 int __fwk_put_event_light(struct fwk_event_light *event)
412 {
413     int status = FWK_E_PARAM;
414     enum interrupt_states intr_state;
415 
416 #ifdef BUILD_MODE_DEBUG
417     if (!ctx.initialized) {
418         status = FWK_E_INIT;
419         goto error;
420     }
421 
422     if (event == NULL) {
423         goto error;
424     }
425 #endif
426 
427     if (fwk_is_interrupt_context()) {
428         intr_state = INTERRUPT_STATE;
429     } else {
430         intr_state = NOT_INTERRUPT_STATE;
431     }
432 
433     if ((intr_state == NOT_INTERRUPT_STATE) && (ctx.current_event != NULL)) {
434         event->source_id = ctx.current_event->target_id;
435     } else if (
436         !fwk_id_type_is_valid(event->source_id) ||
437         !fwk_module_is_valid_entity_id(event->source_id)) {
438         status = FWK_E_PARAM;
439         goto error;
440     }
441 
442 #ifdef BUILD_MODE_DEBUG
443     status = FWK_E_PARAM;
444 
445     if (!fwk_module_is_valid_event_id(event->id)) {
446         goto error;
447     }
448 
449     if (fwk_id_get_module_idx(event->target_id) !=
450         fwk_id_get_module_idx(event->id)) {
451         goto error;
452     }
453 
454 #endif
455     return put_event(event, intr_state, FWK_EVENT_TYPE_LIGHT);
456 
457 error:
458     FWK_LOG_CRIT(err_msg_func, status, __func__);
459     return status;
460 }
461