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