1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *     Notification facilities.
9  */
10 
11 #include <internal/fwk_core.h>
12 #include <internal/fwk_module.h>
13 #include <internal/fwk_notification.h>
14 
15 #include <fwk_assert.h>
16 #include <fwk_attributes.h>
17 #include <fwk_dlist.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_notification.h>
26 #include <fwk_status.h>
27 
28 #include <stdbool.h>
29 #include <stddef.h>
30 
31 struct notification_ctx {
32     /*
33      * Queue of notification subscription structures that are free.
34      */
35     struct fwk_dlist free_subscription_dlist;
36 };
37 
38 static struct notification_ctx ctx;
39 
40 static const char err_msg_func[] = "[NOT] Error %d in %s";
41 
42 /*
43  * Static functions
44  */
45 
46 /*
47  * Get list of subscriptions for a given notification emitted by a given source
48  *
49  * \note The function assumes the validity of all its input parameters.
50  *
51  * \param notification_id Identifier of the notification.
52  * \param source_id Identifier of the emitter of the notification.
53  *
54  * \return A pointer to the doubly-linked list of subscriptions.
55  */
get_subscription_dlist(fwk_id_t notification_id,fwk_id_t source_id)56 static struct fwk_dlist *get_subscription_dlist(
57     fwk_id_t notification_id, fwk_id_t source_id)
58 {
59     struct fwk_dlist *subscription_dlist_table;
60 
61     if (fwk_id_is_type(source_id, FWK_ID_TYPE_MODULE)) {
62         subscription_dlist_table =
63             fwk_module_get_ctx(source_id)->subscription_dlist_table;
64     } else {
65         subscription_dlist_table =
66             fwk_module_get_element_ctx(source_id)->subscription_dlist_table;
67     }
68 
69     return &subscription_dlist_table[
70                fwk_id_get_notification_idx(notification_id)];
71 }
72 
73 /*
74  * Search for a subscription with a given source and target identifier in a list
75  * of subscriptions.
76  *
77  * \note The function assumes the validity of all its input parameters.
78  *
79  * \param subscription_dlist Pointer to the doubly-linked list of subscriptions
80  *      to search.
81  * \param source_id Identifier of the emitter of the notification.
82  * \param target_id Identifier of the target of the notification.
83  *
84  * \return A pointer to the subscription that has been found if any, NULL
85  *      otherwise.
86  */
search_subscription(struct fwk_dlist * subscription_dlist,fwk_id_t source_id,fwk_id_t target_id)87 static struct __fwk_notification_subscription *search_subscription(
88     struct fwk_dlist *subscription_dlist,
89     fwk_id_t source_id, fwk_id_t target_id)
90 {
91     struct fwk_dlist_node *node;
92     struct __fwk_notification_subscription *subscription;
93 
94     for (node = fwk_list_head(subscription_dlist); node != NULL;
95          node = fwk_list_next(subscription_dlist, node)) {
96         subscription = FWK_LIST_GET(node,
97             struct __fwk_notification_subscription, dlist_node);
98 
99         if (fwk_id_is_equal(subscription->source_id, source_id) &&
100             fwk_id_is_equal(subscription->target_id, target_id)) {
101             return subscription;
102         }
103     }
104 
105     return NULL;
106 }
107 
108 /*
109  * Send all the notifications associated with a notification event.
110  *
111  * \note The function assumes the validity of all its input parameters. The
112  *      function is a sub-routine of 'fwk_notification_notify'.
113  *
114  * \param notification_event Pointer to the notification event.
115  * \param[out] count The number of notifications being sent.
116  */
send_notifications(struct fwk_event * notification_event,unsigned int * count)117 static void send_notifications(struct fwk_event *notification_event,
118                                unsigned int *count)
119 {
120     int status;
121     struct fwk_dlist *subscription_dlist;
122     struct fwk_dlist_node *node;
123     struct __fwk_notification_subscription *subscription;
124 
125     subscription_dlist = get_subscription_dlist(notification_event->id,
126                                                 notification_event->source_id);
127     notification_event->is_response = false;
128     notification_event->is_notification = true;
129 
130     for (node = fwk_list_head(subscription_dlist); node != NULL;
131          node = fwk_list_next(subscription_dlist, node)) {
132         subscription = FWK_LIST_GET(node,
133             struct __fwk_notification_subscription, dlist_node);
134 
135         if (!fwk_id_is_equal(
136                 subscription->source_id, notification_event->source_id)) {
137             continue;
138         }
139 
140         notification_event->target_id = subscription->target_id;
141 
142         status = __fwk_put_notification(notification_event);
143         if (status == FWK_SUCCESS) {
144             (*count)++;
145         }
146     }
147 }
148 
149 /*
150  * Private interface functions
151  */
152 
fwk_notification_init(void)153 static FWK_CONSTRUCTOR void fwk_notification_init(void)
154 {
155     static struct __fwk_notification_subscription
156         subscriptions[FMW_NOTIFICATION_MAX];
157 
158     unsigned int i;
159 
160     /* All the subscription structures are free to be used */
161     fwk_list_init(&ctx.free_subscription_dlist);
162 
163     for (i = 0; i < FMW_NOTIFICATION_MAX; i++) {
164         fwk_list_push_tail(
165             &ctx.free_subscription_dlist, &subscriptions[i].dlist_node);
166     }
167 }
168 
__fwk_notification_reset(void)169 void __fwk_notification_reset(void)
170 {
171     fwk_notification_init();
172 }
173 
174 /*
175  * Public interface functions
176  */
177 
fwk_notification_subscribe(fwk_id_t notification_id,fwk_id_t source_id,fwk_id_t target_id)178 int fwk_notification_subscribe(fwk_id_t notification_id, fwk_id_t source_id,
179                                fwk_id_t target_id)
180 {
181     int status;
182     unsigned int flags;
183     struct fwk_dlist *subscription_dlist;
184     struct __fwk_notification_subscription *subscription;
185 
186     if (fwk_is_interrupt_context()) {
187         status = FWK_E_HANDLER;
188         goto error;
189     }
190 
191     if (!fwk_module_is_valid_notification_id(notification_id) ||
192         !fwk_module_is_valid_entity_id(source_id) ||
193         !fwk_module_is_valid_entity_id(target_id) ||
194         !fwk_id_is_equal(fwk_id_build_module_id(notification_id),
195                          fwk_id_build_module_id(source_id))) {
196         status = FWK_E_PARAM;
197         goto error;
198     }
199 
200     subscription_dlist = get_subscription_dlist(notification_id, source_id);
201     if (search_subscription(subscription_dlist, source_id, target_id) != NULL) {
202         status = FWK_E_STATE;
203         goto error;
204     }
205 
206     subscription = FWK_LIST_GET(
207         fwk_list_pop_head(&ctx.free_subscription_dlist),
208         struct __fwk_notification_subscription, dlist_node);
209 
210     if (subscription == NULL) {
211         status = FWK_E_NOMEM;
212         fwk_unexpected();
213         goto error;
214     }
215 
216     subscription->source_id = source_id;
217     subscription->target_id = target_id;
218 
219     flags = fwk_interrupt_global_disable();
220     fwk_list_push_tail(subscription_dlist, &subscription->dlist_node);
221     (void)fwk_interrupt_global_enable(flags);
222 
223     return FWK_SUCCESS;
224 
225 error:
226     FWK_LOG_CRIT(err_msg_func, status, __func__);
227     return status;
228 }
229 
fwk_notification_unsubscribe(fwk_id_t notification_id,fwk_id_t source_id,fwk_id_t target_id)230 int fwk_notification_unsubscribe(fwk_id_t notification_id, fwk_id_t source_id,
231                                  fwk_id_t target_id)
232 {
233     int status;
234     unsigned int flags;
235     struct fwk_dlist *subscription_dlist;
236     struct __fwk_notification_subscription *subscription;
237 
238     if (fwk_is_interrupt_context()) {
239         status = FWK_E_HANDLER;
240         goto error;
241     }
242 
243     if (!(fwk_module_is_valid_notification_id(notification_id)) ||
244         !(fwk_module_is_valid_entity_id(source_id)) ||
245         !(fwk_module_is_valid_entity_id(target_id)) ||
246         !fwk_id_is_equal(fwk_id_build_module_id(notification_id),
247                          fwk_id_build_module_id(source_id))) {
248         status = FWK_E_PARAM;
249         goto error;
250     }
251 
252     subscription_dlist = get_subscription_dlist(notification_id, source_id);
253     subscription = search_subscription(subscription_dlist,
254                                        source_id, target_id);
255     if (subscription == NULL) {
256         status = FWK_E_STATE;
257         goto error;
258     }
259 
260     flags = fwk_interrupt_global_disable();
261     fwk_list_remove(subscription_dlist, &subscription->dlist_node);
262     (void)fwk_interrupt_global_enable(flags);
263     fwk_list_push_tail(&ctx.free_subscription_dlist, &subscription->dlist_node);
264 
265     return FWK_SUCCESS;
266 
267 error:
268     FWK_LOG_CRIT(err_msg_func, status, __func__);
269     return status;
270 }
271 
fwk_notification_notify(struct fwk_event * notification_event,unsigned int * count)272 int fwk_notification_notify(struct fwk_event *notification_event,
273                             unsigned int *count)
274 {
275     int status;
276     const struct fwk_event *current_event;
277 
278     if ((notification_event == NULL) || (count == NULL)) {
279         return FWK_E_PARAM;
280     }
281 
282     if (fwk_is_interrupt_context()) {
283         if (!fwk_module_is_valid_entity_id(notification_event->source_id)) {
284             status = FWK_E_PARAM;
285             goto error;
286         }
287     } else {
288         current_event = __fwk_get_current_event();
289 
290         if ((current_event != NULL) &&
291             (!fwk_module_is_valid_entity_id(notification_event->source_id))) {
292             /*
293              * The source_id provided is not valid, use the identifier of the
294              * target for the current event.
295              */
296             notification_event->source_id = current_event->target_id;
297         }
298     }
299 
300     if (!fwk_module_is_valid_notification_id(notification_event->id) ||
301         (fwk_id_get_module_idx(notification_event->id) !=
302          fwk_id_get_module_idx(notification_event->source_id))) {
303         status = FWK_E_PARAM;
304         goto error;
305     }
306 
307     *count = 0;
308     send_notifications(notification_event, count);
309 
310     return FWK_SUCCESS;
311 
312 error:
313     FWK_LOG_CRIT(err_msg_func, status, __func__);
314     return status;
315 }
316