git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@150 6f19259b...
[mirror_edk2.git] / EdkModulePkg / Core / Dxe / Event / event.c
CommitLineData
e20ee534
LG
1/*++
2
3Copyright (c) 2006, Intel Corporation
4All rights reserved. This program and the accompanying materials
5are licensed and made available under the terms and conditions of the BSD License
6which accompanies this distribution. The full text of the license may be found at
7http://opensource.org/licenses/bsd-license.php
8
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12Module Name:
13
14 event.c
15
16Abstract:
17
18 EFI Event support
19
20--*/
21
22
23#include <DxeMain.h>
24
25//
26// Enumerate the valid types
27//
28UINT32 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
91VOID
92CoreAcquireEventLock (
93 VOID
94 )
95/*++
96
97Routine Description:
98
99 Enter critical section by acquiring the lock on gEventQueueLock.
100
101Arguments:
102
103 None
104
105Returns:
106
107 None
108
109--*/
110{
111 CoreAcquireLock (&gEventQueueLock);
112}
113
114
115VOID
116CoreReleaseEventLock (
117 VOID
118 )
119/*++
120
121Routine Description:
122
123 Exit critical section by releasing the lock on gEventQueueLock.
124
125Arguments:
126
127 None
128
129Returns:
130
131 None
132
133--*/
134{
135 CoreReleaseLock (&gEventQueueLock);
136}
137
138
139EFI_STATUS
140CoreInitializeEventServices (
141 VOID
142 )
143/*++
144
145Routine Description:
146
147 Initializes "event" support and populates parts of the System and Runtime Table.
148
149Arguments:
150
151 None
152
153Returns:
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
171EFI_STATUS
172CoreShutdownEventServices (
173 VOID
174 )
175/*++
176
177Routine Description:
178
179 Register all runtime events to make sure they are still available after ExitBootService.
180
181Arguments:
182
183 None
184
185Returns:
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
215VOID
216CoreDispatchEventNotifies (
217 IN EFI_TPL Priority
218 )
219/*++
220
221Routine Description:
222
223 Dispatches all pending events.
224
225Arguments:
226
227 Priority - The task priority level of event notifications to dispatch
228
229Returns:
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
279VOID
280STATIC
281CoreNotifyEvent (
282 IN IEVENT *Event
283 )
284/*++
285
286Routine Description:
287
288 Queues the event's notification function to fire
289
290Arguments:
291
292 Event - The Event to notify
293
294Returns:
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
325VOID
326CoreNotifySignalList (
327 IN EFI_GUID *EventGroup
328 )
329/*++
330
331Routine Description:
332 Signals all events in the EventGroup
333
334Arguments:
335 EventGroup - The list to signal
336
337Returns:
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
363static
364VOID
365EFIAPI
366EventNofitySignalAllNullEvent (
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
387EFI_STATUS
388EFIAPI
389CoreCreateEvent (
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
398Routine Description:
399 Creates a general-purpose event structure
400
401Arguments:
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
409Returns:
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 = &gEfiEventReadyToBootGuid;
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
455EFI_STATUS
456EFIAPI
457CoreCreateEventEx (
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
467Routine Description:
468 Creates a general-purpose event structure
469
470Arguments:
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
479Returns:
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
584EFI_STATUS
585EFIAPI
586CoreSignalEvent (
587 IN EFI_EVENT UserEvent
588 )
589/*++
590
591Routine Description:
592
593 Signals the event. Queues the event to be notified if needed
594
595Arguments:
596
597 UserEvent - The event to signal
598
599Returns:
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
651EFI_STATUS
652EFIAPI
653CoreCheckEvent (
654 IN EFI_EVENT UserEvent
655 )
656/*++
657
658Routine Description:
659
660 Check the status of an event
661
662Arguments:
663
664 UserEvent - The event to check
665
666Returns:
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
727EFI_STATUS
728EFIAPI
729CoreWaitForEvent (
730 IN UINTN NumberOfEvents,
731 IN EFI_EVENT *UserEvents,
732 OUT UINTN *UserIndex
733 )
734/*++
735
736Routine Description:
737
738 Stops execution until an event is signaled.
739
740Arguments:
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
746Returns:
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
789EFI_STATUS
790EFIAPI
791CoreCloseEvent (
792 IN EFI_EVENT UserEvent
793 )
794/*++
795
796Routine Description:
797
798 Closes an event and frees the event structure.
799
800Arguments:
801
802 UserEvent - Event to close
803
804Returns:
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}