e8391419ab6768e3e2fc9a5c1f0bb7ee7ab2b422
[mirror_edk2.git] / EdkModulePkg / Core / Dxe / Event / event.c
1 /*++
2
3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 event.c
15
16 Abstract:
17
18 EFI Event support
19
20 --*/
21
22
23 #include <DxeMain.h>
24
25 //
26 // Enumerate the valid types
27 //
28 UINT32 mEventTable[] = {
29 //
30 // 0x80000200 Timer event with a notification function that is
31 // queue when the event is signaled with SignalEvent()
32 //
33 EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
34 //
35 // 0x80000000 Timer event without a notification function. It can be
36 // signaled with SignalEvent() and checked with CheckEvent() or WaitForEvent().
37 //
38 EFI_EVENT_TIMER,
39 //
40 // 0x00000100 Generic event with a notification function that
41 // can be waited on with CheckEvent() or WaitForEvent()
42 //
43 EFI_EVENT_NOTIFY_WAIT,
44 //
45 // 0x00000200 Generic event with a notification function that
46 // is queue when the event is signaled with SignalEvent()
47 //
48 EFI_EVENT_NOTIFY_SIGNAL,
49 //
50 // 0x00000201 ExitBootServicesEvent.
51 //
52 EFI_EVENT_SIGNAL_EXIT_BOOT_SERVICES,
53 //
54 // 0x60000202 SetVirtualAddressMapEvent.
55 //
56 EFI_EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
57
58 #if (EFI_SPECIFICATION_VERSION < 0x00020000)
59 //
60 // 0x00000203 ReadyToBootEvent.
61 //
62 EFI_EVENT_SIGNAL_READY_TO_BOOT,
63 //
64 // 0x00000204 LegacyBootEvent.
65 //
66 EFI_EVENT_SIGNAL_LEGACY_BOOT,
67 //
68 // 0x00000603 Signal all ReadyToBootEvents.
69 //
70 EFI_EVENT_NOTIFY_SIGNAL_ALL | EFI_EVENT_SIGNAL_READY_TO_BOOT,
71 //
72 // 0x00000604 Signal all LegacyBootEvents.
73 //
74 EFI_EVENT_NOTIFY_SIGNAL_ALL | EFI_EVENT_SIGNAL_LEGACY_BOOT,
75 #endif
76
77 //
78 // 0x00000000 Generic event without a notification function.
79 // It can be signaled with SignalEvent() and checked with CheckEvent()
80 // or WaitForEvent().
81 //
82 0x00000000,
83 //
84 // 0x80000100 Timer event with a notification function that can be
85 // waited on with CheckEvent() or WaitForEvent()
86 //
87 EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_WAIT,
88 };
89
90
91 VOID
92 CoreAcquireEventLock (
93 VOID
94 )
95 /*++
96
97 Routine Description:
98
99 Enter critical section by acquiring the lock on gEventQueueLock.
100
101 Arguments:
102
103 None
104
105 Returns:
106
107 None
108
109 --*/
110 {
111 CoreAcquireLock (&gEventQueueLock);
112 }
113
114
115 VOID
116 CoreReleaseEventLock (
117 VOID
118 )
119 /*++
120
121 Routine Description:
122
123 Exit critical section by releasing the lock on gEventQueueLock.
124
125 Arguments:
126
127 None
128
129 Returns:
130
131 None
132
133 --*/
134 {
135 CoreReleaseLock (&gEventQueueLock);
136 }
137
138
139 EFI_STATUS
140 CoreInitializeEventServices (
141 VOID
142 )
143 /*++
144
145 Routine Description:
146
147 Initializes "event" support and populates parts of the System and Runtime Table.
148
149 Arguments:
150
151 None
152
153 Returns:
154
155 EFI_SUCCESS - Always return success
156
157 --*/
158 {
159 UINTN Index;
160
161 for (Index=0; Index <= EFI_TPL_HIGH_LEVEL; Index++) {
162 InitializeListHead (&gEventQueue[Index]);
163 }
164
165 CoreInitializeTimer ();
166
167 return EFI_SUCCESS;
168 }
169
170
171 EFI_STATUS
172 CoreShutdownEventServices (
173 VOID
174 )
175 /*++
176
177 Routine Description:
178
179 Register all runtime events to make sure they are still available after ExitBootService.
180
181 Arguments:
182
183 None
184
185 Returns:
186
187 EFI_SUCCESS - Always return success.
188
189 --*/
190 {
191 LIST_ENTRY *Link;
192 IEVENT *Event;
193
194 //
195 // The Runtime AP is required for the core to function!
196 //
197 ASSERT (gRuntime != NULL);
198
199 for (Link = mRuntimeEventList.ForwardLink; Link != &mRuntimeEventList; Link = Link->ForwardLink) {
200 Event = CR (Link, IEVENT, RuntimeLink, EVENT_SIGNATURE);
201 gRuntime->RegisterEvent (
202 gRuntime,
203 Event->Type,
204 Event->NotifyTpl,
205 Event->NotifyFunction,
206 Event->NotifyContext,
207 (VOID **)Event
208 );
209 }
210
211 return EFI_SUCCESS;
212 }
213
214
215 VOID
216 CoreDispatchEventNotifies (
217 IN EFI_TPL Priority
218 )
219 /*++
220
221 Routine Description:
222
223 Dispatches all pending events.
224
225 Arguments:
226
227 Priority - The task priority level of event notifications to dispatch
228
229 Returns:
230
231 None
232
233 --*/
234 {
235 IEVENT *Event;
236 LIST_ENTRY *Head;
237
238 CoreAcquireEventLock ();
239 ASSERT (gEventQueueLock.OwnerTpl == Priority);
240 Head = &gEventQueue[Priority];
241
242 //
243 // Dispatch all the pending notifications
244 //
245 while (!IsListEmpty (Head)) {
246
247 Event = CR (Head->ForwardLink, IEVENT, NotifyLink, EVENT_SIGNATURE);
248 RemoveEntryList (&Event->NotifyLink);
249
250 Event->NotifyLink.ForwardLink = NULL;
251
252 //
253 // Only clear the SIGNAL status if it is a SIGNAL type event.
254 // WAIT type events are only cleared in CheckEvent()
255 //
256 if (Event->Type & EFI_EVENT_NOTIFY_SIGNAL) {
257 Event->SignalCount = 0;
258 }
259
260 CoreReleaseEventLock ();
261
262 //
263 // Notify this event
264 //
265 ASSERT (Event->NotifyFunction != NULL);
266 Event->NotifyFunction (Event, Event->NotifyContext);
267
268 //
269 // Check for next pending event
270 //
271 CoreAcquireEventLock ();
272 }
273
274 gEventPending &= ~(1 << Priority);
275 CoreReleaseEventLock ();
276 }
277
278
279 VOID
280 STATIC
281 CoreNotifyEvent (
282 IN IEVENT *Event
283 )
284 /*++
285
286 Routine Description:
287
288 Queues the event's notification function to fire
289
290 Arguments:
291
292 Event - The Event to notify
293
294 Returns:
295
296 None
297
298 --*/
299 {
300
301 //
302 // Event database must be locked
303 //
304 ASSERT_LOCKED (&gEventQueueLock);
305
306 //
307 // If the event is queued somewhere, remove it
308 //
309
310 if (Event->NotifyLink.ForwardLink != NULL) {
311 RemoveEntryList (&Event->NotifyLink);
312 Event->NotifyLink.ForwardLink = NULL;
313 }
314
315 //
316 // Queue the event to the pending notification list
317 //
318
319 InsertTailList (&gEventQueue[Event->NotifyTpl], &Event->NotifyLink);
320 gEventPending |= (UINTN)(1 << Event->NotifyTpl);
321 }
322
323
324
325 VOID
326 CoreNotifySignalList (
327 IN EFI_GUID *EventGroup
328 )
329 /*++
330
331 Routine Description:
332 Signals all events in the EventGroup
333
334 Arguments:
335 EventGroup - The list to signal
336
337 Returns:
338
339 None
340
341 --*/
342 {
343 LIST_ENTRY *Link;
344 LIST_ENTRY *Head;
345 IEVENT *Event;
346
347 CoreAcquireEventLock ();
348
349 Head = &gEventSignalQueue;
350 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
351 Event = CR (Link, IEVENT, SignalLink, EVENT_SIGNATURE);
352 if (CompareGuid (&Event->EventGroup, EventGroup)) {
353 CoreNotifyEvent (Event);
354 }
355 }
356
357 CoreReleaseEventLock ();
358 }
359
360
361 #if (EFI_SPECIFICATION_VERSION < 0x00020000)
362
363 static
364 VOID
365 EFIAPI
366 EventNofitySignalAllNullEvent (
367 IN EFI_EVENT Event,
368 IN VOID *Context
369 )
370 {
371 //
372 // This null event is a size efficent way to enusre that
373 // EFI_EVENT_NOTIFY_SIGNAL_ALL is error checked correctly.
374 // EFI_EVENT_NOTIFY_SIGNAL_ALL is now mapped into
375 // CreateEventEx() and this function is used to make the
376 // old error checking in CreateEvent() for Tiano extensions
377 // function.
378 //
379 return;
380 }
381
382 #endif
383
384
385
386
387 EFI_STATUS
388 EFIAPI
389 CoreCreateEvent (
390 IN UINT32 Type,
391 IN EFI_TPL NotifyTpl,
392 IN EFI_EVENT_NOTIFY NotifyFunction, OPTIONAL
393 IN VOID *NotifyContext, OPTIONAL
394 OUT EFI_EVENT *Event
395 )
396 /*++
397
398 Routine Description:
399 Creates a general-purpose event structure
400
401 Arguments:
402 Type - The type of event to create and its mode and attributes
403 NotifyTpl - The task priority level of event notifications
404 NotifyFunction - Pointer to the events notification function
405 NotifyContext - Pointer to the notification functions context; corresponds to
406 parameter "Context" in the notification function
407 Event - Pointer to the newly created event if the call succeeds; undefined otherwise
408
409 Returns:
410 EFI_SUCCESS - The event structure was created
411 EFI_INVALID_PARAMETER - One of the parameters has an invalid value
412 EFI_OUT_OF_RESOURCES - The event could not be allocated
413
414 --*/
415 {
416 EFI_GUID *GuidPtr;
417 EFI_EVENT_NOTIFY Function;
418
419 GuidPtr = NULL;
420 Function = NotifyFunction;
421
422 #if (EFI_SPECIFICATION_VERSION < 0x00020000)
423 //
424 // Clear EFI_EVENT_NOFITY_SIGNAL_ALL (Tiano extension) as all events in the
425 // EventGroup now have this property. So we need to filter it out.
426 //
427 if (Type & EFI_EVENT_NOTIFY_SIGNAL_ALL) {
428 Type &= ~EFI_EVENT_NOTIFY_SIGNAL_ALL;
429 Function = EventNofitySignalAllNullEvent;
430 }
431
432 //
433 // Map the Tiano extensions Events to CreateEventEx form
434 //
435 if (Type == EFI_EVENT_SIGNAL_READY_TO_BOOT) {
436 GuidPtr = &gEfiEventReadToBootGuid;
437 } else if (Type == EFI_EVENT_SIGNAL_LEGACY_BOOT) {
438 GuidPtr = &gEfiEventLegacyBootGuid
439 }
440 #endif
441
442 //
443 // Convert EFI 1.10 Events to thier UEFI 2.0 CreateEventEx mapping
444 //
445 if (Type == EVENT_SIGNAL_EXIT_BOOT_SERVICES) {
446 GuidPtr = &gEfiEventExitBootServicesGuid;
447 } else if (Type == EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) {
448 GuidPtr = &gEfiEventVirtualAddressChangeGuid;
449 }
450
451 return CoreCreateEventEx (Type, NotifyTpl, Function, NotifyContext, GuidPtr, Event);
452 }
453
454
455 EFI_STATUS
456 EFIAPI
457 CoreCreateEventEx (
458 IN UINT32 Type,
459 IN EFI_TPL NotifyTpl,
460 IN EFI_EVENT_NOTIFY NotifyFunction, OPTIONAL
461 IN CONST VOID *NotifyContext, OPTIONAL
462 IN CONST EFI_GUID *EventGroup, OPTIONAL
463 OUT EFI_EVENT *Event
464 )
465 /*++
466
467 Routine Description:
468 Creates a general-purpose event structure
469
470 Arguments:
471 Type - The type of event to create and its mode and attributes
472 NotifyTpl - The task priority level of event notifications
473 NotifyFunction - Pointer to the events notification function
474 NotifyContext - Pointer to the notification functions context; corresponds to
475 parameter "Context" in the notification function
476 EventGrout - GUID for EventGroup if NULL act the same as gBS->CreateEvent().
477 Event - Pointer to the newly created event if the call succeeds; undefined otherwise
478
479 Returns:
480 EFI_SUCCESS - The event structure was created
481 EFI_INVALID_PARAMETER - One of the parameters has an invalid value
482 EFI_OUT_OF_RESOURCES - The event could not be allocated
483
484 --*/
485 {
486 EFI_STATUS Status;
487 IEVENT *IEvent;
488 INTN Index;
489
490
491 if ((Event == NULL) || (NotifyTpl == EFI_TPL_APPLICATION)) {
492 return EFI_INVALID_PARAMETER;
493 }
494
495 //
496 // Check to make sure no reserved flags are set
497 //
498 Status = EFI_INVALID_PARAMETER;
499 for (Index = 0; Index < (sizeof (mEventTable) / sizeof (UINT32)); Index++) {
500 if (Type == mEventTable[Index]) {
501 Status = EFI_SUCCESS;
502 break;
503 }
504 }
505 if(EFI_ERROR (Status)) {
506 return EFI_INVALID_PARAMETER;
507 }
508
509 //
510 // If it's a notify type of event, check its parameters
511 //
512 if ((Type & (EFI_EVENT_NOTIFY_WAIT | EFI_EVENT_NOTIFY_SIGNAL))) {
513 //
514 // Check for an invalid NotifyFunction or NotifyTpl
515 //
516 if ((NotifyFunction == NULL) ||
517 (NotifyTpl < EFI_TPL_APPLICATION) ||
518 (NotifyTpl >= EFI_TPL_HIGH_LEVEL)) {
519 return EFI_INVALID_PARAMETER;
520 }
521
522 } else {
523 //
524 // No notification needed, zero ignored values
525 //
526 NotifyTpl = 0;
527 NotifyFunction = NULL;
528 NotifyContext = NULL;
529 }
530
531 //
532 // Allcoate and initialize a new event structure.
533 //
534 Status = CoreAllocatePool (
535 (Type & EFI_EVENT_RUNTIME) ? EfiRuntimeServicesData: EfiBootServicesData,
536 sizeof (IEVENT),
537 (VOID **)&IEvent
538 );
539 if (EFI_ERROR (Status)) {
540 return EFI_OUT_OF_RESOURCES;
541 }
542
543 SetMem (IEvent, sizeof (IEVENT), 0);
544
545 IEvent->Signature = EVENT_SIGNATURE;
546 IEvent->Type = Type;
547
548 IEvent->NotifyTpl = NotifyTpl;
549 IEvent->NotifyFunction = NotifyFunction;
550 IEvent->NotifyContext = (VOID *)NotifyContext;
551 if (EventGroup != NULL) {
552 CopyGuid (&IEvent->EventGroup, EventGroup);
553 IEvent->ExFlag = TRUE;
554 }
555
556 *Event = IEvent;
557
558 if (Type & EFI_EVENT_RUNTIME) {
559 //
560 // Keep a list of all RT events so we can tell the RT AP.
561 //
562 InsertTailList (&mRuntimeEventList, &IEvent->RuntimeLink);
563 }
564
565 CoreAcquireEventLock ();
566
567 if ((Type & EFI_EVENT_NOTIFY_SIGNAL) != 0x00000000) {
568 //
569 // The Event's NotifyFunction must be queued whenever the event is signaled
570 //
571 InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
572 }
573
574 CoreReleaseEventLock ();
575
576 //
577 // Done
578 //
579 return EFI_SUCCESS;
580 }
581
582
583
584 EFI_STATUS
585 EFIAPI
586 CoreSignalEvent (
587 IN EFI_EVENT UserEvent
588 )
589 /*++
590
591 Routine Description:
592
593 Signals the event. Queues the event to be notified if needed
594
595 Arguments:
596
597 UserEvent - The event to signal
598
599 Returns:
600
601 EFI_INVALID_PARAMETER - Parameters are not valid.
602
603 EFI_SUCCESS - The event was signaled.
604
605 --*/
606 {
607 IEVENT *Event;
608
609 Event = UserEvent;
610
611 if (Event == NULL) {
612 return EFI_INVALID_PARAMETER;
613 }
614
615 if (Event->Signature != EVENT_SIGNATURE) {
616 return EFI_INVALID_PARAMETER;
617 }
618
619 CoreAcquireEventLock ();
620
621 //
622 // If the event is not already signalled, do so
623 //
624
625 if (Event->SignalCount == 0x00000000) {
626 Event->SignalCount++;
627
628 //
629 // If signalling type is a notify function, queue it
630 //
631 if (Event->Type & EFI_EVENT_NOTIFY_SIGNAL) {
632 if (Event->ExFlag) {
633 //
634 // The CreateEventEx() style requires all members of the Event Group
635 // to be signaled.
636 //
637 CoreReleaseEventLock ();
638 CoreNotifySignalList (&Event->EventGroup);
639 CoreAcquireEventLock ();
640 } else {
641 CoreNotifyEvent (Event);
642 }
643 }
644 }
645
646 CoreReleaseEventLock ();
647 return EFI_SUCCESS;
648 }
649
650
651 EFI_STATUS
652 EFIAPI
653 CoreCheckEvent (
654 IN EFI_EVENT UserEvent
655 )
656 /*++
657
658 Routine Description:
659
660 Check the status of an event
661
662 Arguments:
663
664 UserEvent - The event to check
665
666 Returns:
667
668 EFI_SUCCESS - The event is in the signaled state
669 EFI_NOT_READY - The event is not in the signaled state
670 EFI_INVALID_PARAMETER - Event is of type EVT_NOTIFY_SIGNAL
671
672 --*/
673
674 {
675 IEVENT *Event;
676 EFI_STATUS Status;
677
678 Event = UserEvent;
679
680 if (Event == NULL) {
681 return EFI_INVALID_PARAMETER;
682 }
683
684 if (Event->Signature != EVENT_SIGNATURE) {
685 return EFI_INVALID_PARAMETER;
686 }
687
688 if (Event->Type & EFI_EVENT_NOTIFY_SIGNAL) {
689 return EFI_INVALID_PARAMETER;
690 }
691
692 Status = EFI_NOT_READY;
693
694 if (!Event->SignalCount && (Event->Type & EFI_EVENT_NOTIFY_WAIT)) {
695
696 //
697 // Queue the wait notify function
698 //
699
700 CoreAcquireEventLock ();
701 if (!Event->SignalCount) {
702 CoreNotifyEvent (Event);
703 }
704 CoreReleaseEventLock ();
705 }
706
707 //
708 // If the even looks signalled, get the lock and clear it
709 //
710
711 if (Event->SignalCount) {
712 CoreAcquireEventLock ();
713
714 if (Event->SignalCount) {
715 Event->SignalCount = 0;
716 Status = EFI_SUCCESS;
717 }
718
719 CoreReleaseEventLock ();
720 }
721
722 return Status;
723 }
724
725
726
727 EFI_STATUS
728 EFIAPI
729 CoreWaitForEvent (
730 IN UINTN NumberOfEvents,
731 IN EFI_EVENT *UserEvents,
732 OUT UINTN *UserIndex
733 )
734 /*++
735
736 Routine Description:
737
738 Stops execution until an event is signaled.
739
740 Arguments:
741
742 NumberOfEvents - The number of events in the UserEvents array
743 UserEvents - An array of EFI_EVENT
744 UserIndex - Pointer to the index of the event which satisfied the wait condition
745
746 Returns:
747
748 EFI_SUCCESS - The event indicated by Index was signaled.
749 EFI_INVALID_PARAMETER - The event indicated by Index has a notification function or
750 Event was not a valid type
751 EFI_UNSUPPORTED - The current TPL is not TPL_APPLICATION
752
753 --*/
754
755 {
756 EFI_STATUS Status;
757 UINTN Index;
758
759 //
760 // Can only WaitForEvent at TPL_APPLICATION
761 //
762 if (gEfiCurrentTpl != EFI_TPL_APPLICATION) {
763 return EFI_UNSUPPORTED;
764 }
765
766 for(;;) {
767
768 for(Index = 0; Index < NumberOfEvents; Index++) {
769
770 Status = CoreCheckEvent (UserEvents[Index]);
771
772 //
773 // provide index of event that caused problem
774 //
775 if (Status != EFI_NOT_READY) {
776 *UserIndex = Index;
777 return Status;
778 }
779 }
780
781 //
782 // This was the location of the Idle loop callback in EFI 1.x reference
783 // code. We don't have that concept in this base at this point.
784 //
785 }
786 }
787
788
789 EFI_STATUS
790 EFIAPI
791 CoreCloseEvent (
792 IN EFI_EVENT UserEvent
793 )
794 /*++
795
796 Routine Description:
797
798 Closes an event and frees the event structure.
799
800 Arguments:
801
802 UserEvent - Event to close
803
804 Returns:
805
806 EFI_INVALID_PARAMETER - Parameters are not valid.
807
808 EFI_SUCCESS - The event has been closed
809
810 --*/
811
812 {
813 EFI_STATUS Status;
814 IEVENT *Event;
815
816 Event = UserEvent;
817
818 if (Event == NULL) {
819 return EFI_INVALID_PARAMETER;
820 }
821
822 if (Event->Signature != EVENT_SIGNATURE) {
823 return EFI_INVALID_PARAMETER;
824 }
825
826 //
827 // If it's a timer event, make sure it's not pending
828 //
829 if (Event->Type & EFI_EVENT_TIMER) {
830 CoreSetTimer (Event, TimerCancel, 0);
831 }
832
833 CoreAcquireEventLock ();
834
835 //
836 // If the event is queued somewhere, remove it
837 //
838
839 if (Event->RuntimeLink.ForwardLink != NULL) {
840 RemoveEntryList (&Event->RuntimeLink);
841 }
842
843 if (Event->NotifyLink.ForwardLink != NULL) {
844 RemoveEntryList (&Event->NotifyLink);
845 }
846
847 if (Event->SignalLink.ForwardLink != NULL) {
848 RemoveEntryList (&Event->SignalLink);
849 }
850
851 CoreReleaseEventLock ();
852
853 //
854 // If the event is registered on a protocol notify, then remove it from the protocol database
855 //
856 CoreUnregisterProtocolNotify (Event);
857
858 Status = CoreFreePool (Event);
859 ASSERT_EFI_ERROR (Status);
860
861 return Status;
862 }