1 /*++
2   This file contains 'Framework Code' and is licensed as such
3   under the terms of your license agreement with Intel or your
4   vendor.  This file may not be modified, except as allowed by
5   additional terms of your license agreement.
6 --*/
7 /*++
8 
9 Copyright (c)  2010  - 2014, Intel Corporation. All rights reserved
10 
11 
12   This program and the accompanying materials are licensed and made available under
13 
14   the terms and conditions of the BSD License that accompanies this distribution.
15 
16   The full text of the license may be found at
17 
18   http://opensource.org/licenses/bsd-license.php.
19 
20 
21 
22   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
23 
24   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
25 
26 
27 
28 
29 
30 Module Name:
31 
32   Observable.c
33 
34 Abstract:
35 
36   The following contains all of the implementation for the Observable protocol. The
37   protocol uses the observer design pattern to provide a way to publish events and
38   to subscribe to those events so that a callback will be performed at the time of
39   the event. The observables and subscribers are maintained by the static tree,
40   mObservableDb. The difference between this protocol and the existing event protocol
41   that exists within the EFI framework is that this protocol allows for parameters
42   to be passed to the subscribed callbacks that can contain up to date context.
43 
44 --*/
45 
46 #include "Observable.h"
47 
48 static OBS_TREE*                mObservableDb = NULL;
49 static EFI_HANDLE               mObservableHandle = NULL;
50 static OBS_OBSERVABLE_PROTOCOL  mObservable = {
51   AddObservable,
52   RemoveObservable,
53   Subscribe,
54   Unsubscribe,
55   Publish,
56   RemoveAllObservables
57 };
58 
InitializeObservableProtocol(VOID)59 /** Install observable protocol.
60  *
61  * Install interface and initialize the observable protocol.
62  *
63  * @param   VOID          No parameters.
64  *
65  * @return  EFI_SUCCESS   Successfully installed and initialized the protocol.
66  **/
67 EFI_STATUS
68 InitializeObservableProtocol(
69   VOID
70   )
71 {
72   EFI_STATUS  Status;
73 
74   //
75   // Install protocol.
76   //
77   Status = gBS->InstallProtocolInterface (
78                   &mObservableHandle,
79                   &gObservableProtocolGuid,
80                   EFI_NATIVE_INTERFACE,
81                   &mObservable
82                   );
83 
84   return Status;
85 }
86 
DeleteSubscriber(OBS_LEAF * Head)87 /** Deletes a subscriber
88  *
89  * This function removes the subscriber pointed to by Head.
90  *
91  * @param   OBS_TREE*     Head    Points to the current subscriber.
92  *
93  * @return  OBS_TREE*     Returns the tree after successfully removing the subscriber.
94  **/
95 OBS_LEAF*
96 DeleteSubscriber(
97   OBS_LEAF* Head
98   )
99 {
100   OBS_LEAF* Temp;
101 
102   if (Head) {
103     Temp = Head;
104     Head = Head->Next;
105     gBS->FreePool(Temp);
106   }
107 
108   return Head;
109 }
110 
DeleteAllSubscribers(OBS_LEAF * Head)111 /** Finds and deletes all subscribers
112  *
113  * This function iterates recursively through the existing subscribers and delets them all.
114  *
115  * @param   OBS_TREE*     Head    Points to the current subscriber.
116  *
117  * @return  OBS_TREE*     Returns the tree after successfully removing the subscribers.
118  **/
119 OBS_LEAF*
120 DeleteAllSubscribers(
121   OBS_LEAF* Head
122   )
123 {
124   if (Head) {
125     if (Head->Next) {
126       //
127       // We aren't at the end of the list yet.
128       //
129       Head->Next = DeleteAllSubscribers(Head->Next);
130     }
131 
132     //
133     // At the end, so delete the subscriber.
134     //
135     Head = DeleteSubscriber(Head);
136   }
137 
138   return Head;
139 }
140 
DeleteObservable(OBS_TREE * Head)141 /** Deletes an observable
142  *
143  * This function removes the observable pointed to by Head.
144  *
145  * @param   OBS_TREE*     Head    Points to the current observable.
146  *
147  * @return  OBS_TREE*     Returns the tree after successfully removing the observable.
148  **/
149 OBS_TREE*
150 DeleteObservable(
151   OBS_TREE* Head
152   )
153 {
154   OBS_TREE* Temp;
155 
156   if (Head) {
157     Temp = Head;
158     Head = Head->Next;
159     gBS->FreePool(Temp);
160   }
161 
162   return Head;
163 }
164 
165 /** Finds and deletes all observables
DeleteAllObservables(OBS_TREE * Head)166  *
167  * This function iterates recursively through the existing observables database and, starting with
168  * the last most observable, deletes all of its subscribers, then deletes the observable itself.
169  *
170  * @param   OBS_TREE*     Head    Points to the current observable.
171  *
172  * @return  OBS_TREE*     Returns the tree after successfully removing the observables.
173  **/
174 OBS_TREE*
175 DeleteAllObservables(
176   OBS_TREE* Head
177   )
178 {
179   if (Head) {
180     if (Head->Next) {
181       //
182       // We aren't at the end of the list yet.
183       //
184       Head->Next = DeleteAllObservables(Head->Next);
185     }
186 
187     //
188     // This is the end of the list of observables.
189     //
190     Head->Leaf = DeleteAllSubscribers(Head->Leaf);
191 
192     //
193     // Subscribers are deleted, so now delete the observable.
194     //
195     Head = DeleteObservable(Head);
196   }
197 
198   return Head;
199 }
200 
201 /** Finds and deletes observable
202  *
203  * This function iterates recursively through the existing observable database in order to find the one
FindAndDeleteObservable(OBS_TREE * Head,EFI_GUID ReferenceGuid)204  * specified by ReferenceGuid so that it can be deleted. If the requested observable is found, before it
205  * is deleted, all of the subscribers that are listening to this observable are deleted.
206  *
207  * @param   OBS_TREE*     Head              Points to the current observable.
208  *          EFI_GUID      ReferenceGuid     Corresponds to the observable that we're looking for.
209  *
210  * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the observable.
211  **/
212 OBS_TREE*
213 FindAndDeleteObservable(
214   OBS_TREE* Head,
215   EFI_GUID  ReferenceGuid
216   )
217 {
218   if (Head) {
219     if (CompareMem(&(Head->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
220       //
221       // We found the observable. Delete all of it's subscribers, first.
222       //
223       Head->Leaf = DeleteAllSubscribers(Head->Leaf);
224       //
225       // Now we can safely remove the observable.
226       //
227       Head = DeleteObservable(Head);
228     } else {
229       //
230       // Not found. Keep searching.
231       //
232       Head->Next = FindAndDeleteObservable(Head->Next, ReferenceGuid);
233     }
234   }
235 
236   return Head;
237 }
238 
239 /** Finds and deletes subscriber
240  *
_FindAndDeleteSubscriber(OBS_LEAF * Head,OBS_CALLBACK CallbackInterface)241  * This function iterates recursively through the existing subscribers that are listening to the
242  * observable that was found when this function was called.
243  *
244  * @param   OBS_TREE*     Head              Points to the current subscriber.
245  *          OBS_CALLBACK  CallbackInterface This is the subscriber that is requested be removed.
246  *
247  * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the subscriber.
248  **/
249 OBS_LEAF*
250 _FindAndDeleteSubscriber(
251   OBS_LEAF*     Head,
252   OBS_CALLBACK  CallbackInterface
253   )
254 {
255   if (Head) {
256     if (Head->Observer == CallbackInterface) {
257       //
258       // Found it. Now let's delete it.
259       //
260       Head = DeleteSubscriber(Head);
261     } else {
262       //
263       // Not found. Keep searching.
264       //
265       Head->Next = _FindAndDeleteSubscriber(Head->Next, CallbackInterface);
266     }
267   }
268 
269   return Head;
270 }
271 
272 /** Finds and deletes subscriber
273  *
274  * This function iterates recursively through the existing observables database until it either finds
275  * a matching guid or reaches the end of the list. After finding a match, it calls a helper function,
276  * _FindAndDeleteSubscriber. At this point, all responsibility for finding and deleting the subscriber
FindAndDeleteSubscriber(IN OUT OBS_TREE * Head,IN EFI_GUID ReferenceGuid,IN OBS_CALLBACK CallbackInterface)277  * lies on the helper function.
278  *
279  * @param   OBS_TREE*     Head              Points to the current observable.
280  *          EFI_GUID      ReferenceGuid     Corresponds to the observable that we're looking for.
281  *          OBS_CALLBACK  CallbackInterface This is the subscriber that is requested be removed.
282  *
283  * @return  OBS_TREE*     Returns the tree after successfully removing (or not finding) the subscriber.
284  **/
285 OBS_TREE*
286 FindAndDeleteSubscriber(
287   IN  OUT OBS_TREE*     Head,
288   IN      EFI_GUID      ReferenceGuid,
289   IN      OBS_CALLBACK  CallbackInterface
290   )
291 {
292   if (Head) {
293     if (CompareMem(&(Head->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
294       //
295       // We found the observer that matches ReferenceGuid. Find and delete the subscriber that is
296       // listening to it.
297       //
298       Head->Leaf = _FindAndDeleteSubscriber(Head->Leaf, CallbackInterface);
299     } else {
300       //
301       // Not found. Keep searching.
302       //
303       Head->Next = FindAndDeleteSubscriber(Head->Next, ReferenceGuid, CallbackInterface);
304     }
305   }
306 
307   return Head;
308 }
309 
310 /** Remove all observables.
RemoveAllObservables(VOID)311  *
312  * Remove all observable guids and all interfaces subscribed to them.
313  *
314  * @param   VOID          No parameters.
315  *
316  * @return  EFI_SUCCESS   Successfully removed all observables and subscribed interfaces.
317  **/
318 EFI_STATUS
319 EFIAPI
320 RemoveAllObservables(
321   VOID
322   )
323 {
324   mObservableDb = DeleteAllObservables(mObservableDb);
325 
326   return EFI_SUCCESS;
327 }
328 
329 /** Subscribe an interface with an observable guid.
330  *
331  * Use this to register a callback function with a guid. The function provided by CallbackInterface will be executed
332  * whenever the appropriate observable instance specified by ReferenceGuid calls Publish.
333  *
334  * @param   EFI_GUID              ReferenceGuid       The observable guid that the callback interface will subscribe to.
Subscribe(IN EFI_GUID ReferenceGuid,IN OBS_CALLBACK CallbackInterface)335  *          OBS_CASLLBACK         CallbackInterface   A pointer to the function that is subscribing to the observable.
336  *
337  * @return  EFI_SUCCESS           Successfully subscribed the interface to the observable guid.
338  *          EFI_NOT_FOUND         No match could be found between the provided guid and existing observables.
339  *          EFI_OUT_OF_RESOURCES  Could not subscribe to this observer due to resource limitations.
340  *          EFI_INVALID_PARAMETER Interface is already subscribed to this observer.
341  **/
342 EFI_STATUS
343 EFIAPI
344 Subscribe (
345   IN      EFI_GUID        ReferenceGuid,
346   IN      OBS_CALLBACK    CallbackInterface
347   )
348 {
349   EFI_STATUS  Status    = EFI_SUCCESS;
350   OBS_TREE*   TempTree  = NULL;
351   OBS_LEAF*   Last      = NULL;
352   OBS_LEAF*   TempLeaf  = NULL;
353   OBS_LEAF*   NewLeaf   = NULL;
354   BOOLEAN     Found     = FALSE;
355 
356   if (mObservableDb != NULL) {
357     //
358     // Find the observable guid that we're looking for.
359     //
360     for (TempTree = mObservableDb; TempTree != NULL; TempTree = TempTree->Next) {
361       if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
362         Found = TRUE;
363         break;
364       }
365     }
366     if (Found) {
367       //
368       // Prepare to add a new leaf.
369       //
370       NewLeaf = AllocateZeroPool(sizeof(OBS_LEAF));
371       if (!NewLeaf) {
372         Status = EFI_OUT_OF_RESOURCES;
373       } else {
374         NewLeaf->Next = NULL;
375         NewLeaf->Observer = CallbackInterface;
376         //
377         // Go to the end of the list of observers.
378         //
379         if (TempTree->Leaf != NULL) {
380           //
381           // First check to see if this is a duplicate observer.
382           //
383           Found = FALSE;
384           TempLeaf = TempTree->Leaf;
385           do {
386             Last = TempLeaf;
387             if (TempLeaf->Observer == CallbackInterface) {
388               //
389               // It is, so let's abort this process.
390               //
391               Found = TRUE;
392               break;
393             }
394             TempLeaf = TempLeaf->Next;
395           } while (TempLeaf != NULL);
396           TempLeaf = Last;
397 
398           //
399           // Check for duplicates.
400           //
401           if (Found) {
402             gBS->FreePool(NewLeaf);
403             Status = EFI_INVALID_PARAMETER;
404           } else {
405             //
406             // At this point, TempLeaf->Next will be the end of the list.
407             //
408             TempLeaf->Next = NewLeaf;
409           }
410         } else {
411           //
412           // There are no observers listening to this guid. Start a new list.
413           //
414           TempTree->Leaf = NewLeaf;
415         }
416       }
417     } else {
418       Status = EFI_NOT_FOUND;
419     }
420   } else {
421     Status = EFI_NOT_FOUND;
422   }
423 
424   return Status;
425 }
426 
427 /** Unsubscribe an interface with an observable guid.
428  *
Unsubscribe(IN EFI_GUID ReferenceGuid,IN OBS_CALLBACK CallbackInterface)429  * Use this to remove an interface from the callback list associated with an observable guid.
430  *
431  * @param   EFI_GUID                ReferenceGuid   The observable guid to unsubscribe the interface from.
432  *          OBS_NOTIFY_INTERFACE    NotifyCallback  A pointer to the interface that is being unsubscribed.
433  *
434  * @return  EFI_SUCCESS           Successfully unsubscribed the interface from the observable guid.
435  **/
436 EFI_STATUS
437 EFIAPI
438 Unsubscribe (
439   IN      EFI_GUID        ReferenceGuid,
440   IN      OBS_CALLBACK    CallbackInterface
441   )
442 {
443   mObservableDb = FindAndDeleteSubscriber(mObservableDb, ReferenceGuid, CallbackInterface);
444 
445   return EFI_SUCCESS;
446 }
447 
448 /** Notify observing functions.
449  *
450  * Use this to notify all functions who are subscribed to the guid specified by ReferenceGuid.
Publish(IN EFI_GUID ReferenceGuid,IN OUT VOID * Data)451  *
452  * @param   EFI_GUID          ReferenceGuid   The observable guid that contains the the list of interfaces to be notified.
453  *          VOID*             Data            Parameter context to be passed to the notification function.
454  *
455  * @return  EFI_SUCCESS       Successfully notified all observers listening to this guid.
456  *          EFI_NOT_FOUND     No match could be found between the provided guid and existing observables.
457  **/
458 EFI_STATUS
459 EFIAPI
460 Publish (
461   IN      EFI_GUID                  ReferenceGuid,
462   IN  OUT VOID*                     Data
463   )
464 {
465   EFI_STATUS  Status    = EFI_SUCCESS;
466   OBS_TREE*   TempTree  = NULL;
467   OBS_LEAF*   TempLeaf  = NULL;
468   BOOLEAN     Found     = FALSE;
469 
470   if (mObservableDb != NULL) {
471     //
472     // Find the observable guid that we're looking for.
473     //
474     for (TempTree = mObservableDb; TempTree != NULL; TempTree = TempTree->Next) {
475       if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
476         Found = TRUE;
477         break;
478       }
479     }
480     if (Found) {
481       //
482       // Notify every listener by performing each provided callback.
483       //
484       for (TempLeaf = TempTree->Leaf; TempLeaf != NULL; TempLeaf = TempLeaf->Next) {
485         if (TempLeaf->Observer != NULL) {
486           //
487           // Execute the callback.
488           //
489           TempLeaf->Observer(Data);
490         }
491       }
492     } else {
493       Status = EFI_NOT_FOUND;
494     }
495   } else {
496     Status = EFI_NOT_FOUND;
497   }
498 
499   return Status;
500 }
501 
502 /** Creates a new observable.
503  *
AddObservable(IN EFI_GUID ReferenceGuid)504  * Create a new observable that can be observed with the use of Subscribe function.
505  *
506  * @param   EFI_GUID              ReferenceGuid   The observable guid to add.
507  *
508  * @return  EFI_SUCCESS           Successfully added observable.
509  *          EFI_INVALID_PARAMETER Observable already exists.
510  **/
511 EFI_STATUS
512 EFIAPI
513 AddObservable (
514   IN      EFI_GUID                  ReferenceGuid
515   )
516 {
517   EFI_STATUS  Status    = EFI_SUCCESS;
518   OBS_TREE*   TempTree  = NULL;
519   OBS_TREE*   Last      = NULL;
520   OBS_TREE*   NewTree   = NULL;
521   BOOLEAN     Found     = FALSE;
522 
523   if (mObservableDb != NULL) {
524     if (mObservableDb->Next != NULL) {
525       //
526       // Iterate to the end of the observable list while checking to see if we aren't creating a duplicate.
527       //
528       TempTree = mObservableDb->Next;
529       do {
530         Last = TempTree;
531         if (CompareMem(&(TempTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid)) == 0) {
532           Found = TRUE;
533           break;
534         }
535         TempTree = TempTree->Next;
536       } while (TempTree != NULL);
537       TempTree = Last;
538     } else {
539       TempTree = mObservableDb;
540     }
541     if (Found) {
542       //
543       // Duplicate, so reject the parameter.
544       //
545       Status = EFI_INVALID_PARAMETER;
546     } else {
547       //
548       // TempTree->Next is our target. Prepare to add a new tree link.
549       //
550       NewTree = AllocateZeroPool(sizeof(OBS_TREE));
551       if (NewTree) {
552         NewTree->Next = NULL;
553         NewTree->Leaf = NULL;
554         CopyMem(&(NewTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid));
555         TempTree->Next = NewTree;
556       } else {
557         Status = EFI_OUT_OF_RESOURCES;
558       }
559     }
560   } else {
561     //
562     // mObservableDb has not been created yet. Let's do that.
563     //
564     NewTree = AllocateZeroPool(sizeof(OBS_TREE));
565     if (NewTree) {
566       NewTree->Next = NULL;
567       NewTree->Leaf = NULL;
568       CopyMem(&(NewTree->ObservableGuid), &ReferenceGuid, sizeof(ReferenceGuid));
569       mObservableDb = NewTree;
570     } else {
571       Status = EFI_OUT_OF_RESOURCES;
572     }
573   }
574 
575   return Status;
576 }
577 
578 /** Remove an observable.
579  *
RemoveObservable(IN EFI_GUID ReferenceGuid)580  * Remove an observable so that it can no longer be subscribed to. In addition, unsubscribe any functions
581  * that are subscribed to this guid.
582  *
583  * @param   EFI_GUID              ReferenceGuid   The observable guid to remove.
584  *
585  * @return  EFI_SUCCESS           Successfully removed observable.
586  **/
587 EFI_STATUS
588 EFIAPI
589 RemoveObservable (
590   IN      EFI_GUID        ReferenceGuid
591   )
592 {
593   mObservableDb = FindAndDeleteObservable(mObservableDb, ReferenceGuid);
594 
595   return EFI_SUCCESS;
596 }
597