2 UEFI Event support functions implemented in this file.
4 Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
14 /// gEfiCurrentTpl - Current Task priority level
16 EFI_TPL gEfiCurrentTpl
= TPL_APPLICATION
;
19 /// gEventQueueLock - Protects the event queues
21 EFI_LOCK gEventQueueLock
= EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL
);
24 /// gEventQueue - A list of event's to notify for each priority level
26 LIST_ENTRY gEventQueue
[TPL_HIGH_LEVEL
+ 1];
29 /// gEventPending - A bitmask of the EventQueues that are pending
31 UINTN gEventPending
= 0;
34 /// gEventSignalQueue - A list of events to signal based on EventGroup type
36 LIST_ENTRY gEventSignalQueue
= INITIALIZE_LIST_HEAD_VARIABLE (gEventSignalQueue
);
39 /// Enumerate the valid types
41 UINT32 mEventTable
[] = {
43 /// 0x80000200 Timer event with a notification function that is
44 /// queue when the event is signaled with SignalEvent()
46 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
48 /// 0x80000000 Timer event without a notification function. It can be
49 /// signaled with SignalEvent() and checked with CheckEvent() or WaitForEvent().
53 /// 0x00000100 Generic event with a notification function that
54 /// can be waited on with CheckEvent() or WaitForEvent()
58 /// 0x00000200 Generic event with a notification function that
59 /// is queue when the event is signaled with SignalEvent()
63 /// 0x00000201 ExitBootServicesEvent.
65 EVT_SIGNAL_EXIT_BOOT_SERVICES
,
67 /// 0x60000202 SetVirtualAddressMapEvent.
69 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
,
72 /// 0x00000000 Generic event without a notification function.
73 /// It can be signaled with SignalEvent() and checked with CheckEvent()
74 /// or WaitForEvent().
78 /// 0x80000100 Timer event with a notification function that can be
79 /// waited on with CheckEvent() or WaitForEvent()
81 EVT_TIMER
| EVT_NOTIFY_WAIT
,
85 /// gIdleLoopEvent - Event which is signalled when the core is idle
87 EFI_EVENT gIdleLoopEvent
= NULL
;
90 Enter critical section by acquiring the lock on gEventQueueLock.
94 CoreAcquireEventLock (
98 CoreAcquireLock (&gEventQueueLock
);
102 Exit critical section by releasing the lock on gEventQueueLock.
106 CoreReleaseEventLock (
110 CoreReleaseLock (&gEventQueueLock
);
114 Initializes "event" support.
116 @retval EFI_SUCCESS Always return success
120 CoreInitializeEventServices (
126 for (Index
= 0; Index
<= TPL_HIGH_LEVEL
; Index
++) {
127 InitializeListHead (&gEventQueue
[Index
]);
130 CoreInitializeTimer ();
135 EfiEventEmptyFunction
,
145 Dispatches all pending events.
147 @param Priority The task priority level of event notifications
152 CoreDispatchEventNotifies (
159 CoreAcquireEventLock ();
160 ASSERT (gEventQueueLock
.OwnerTpl
== Priority
);
161 Head
= &gEventQueue
[Priority
];
164 // Dispatch all the pending notifications
166 while (!IsListEmpty (Head
)) {
167 Event
= CR (Head
->ForwardLink
, IEVENT
, NotifyLink
, EVENT_SIGNATURE
);
168 RemoveEntryList (&Event
->NotifyLink
);
170 Event
->NotifyLink
.ForwardLink
= NULL
;
173 // Only clear the SIGNAL status if it is a SIGNAL type event.
174 // WAIT type events are only cleared in CheckEvent()
176 if ((Event
->Type
& EVT_NOTIFY_SIGNAL
) != 0) {
177 Event
->SignalCount
= 0;
180 CoreReleaseEventLock ();
185 ASSERT (Event
->NotifyFunction
!= NULL
);
186 Event
->NotifyFunction (Event
, Event
->NotifyContext
);
189 // Check for next pending event
191 CoreAcquireEventLock ();
194 gEventPending
&= ~(UINTN
)(1 << Priority
);
195 CoreReleaseEventLock ();
199 Queues the event's notification function to fire.
201 @param Event The Event to notify
210 // Event database must be locked
212 ASSERT_LOCKED (&gEventQueueLock
);
215 // If the event is queued somewhere, remove it
218 if (Event
->NotifyLink
.ForwardLink
!= NULL
) {
219 RemoveEntryList (&Event
->NotifyLink
);
220 Event
->NotifyLink
.ForwardLink
= NULL
;
224 // Queue the event to the pending notification list
227 InsertTailList (&gEventQueue
[Event
->NotifyTpl
], &Event
->NotifyLink
);
228 gEventPending
|= (UINTN
)(1 << Event
->NotifyTpl
);
232 Signals all events in the EventGroup.
234 @param EventGroup The list to signal
238 CoreNotifySignalList (
239 IN EFI_GUID
*EventGroup
246 CoreAcquireEventLock ();
248 Head
= &gEventSignalQueue
;
249 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
250 Event
= CR (Link
, IEVENT
, SignalLink
, EVENT_SIGNATURE
);
251 if (CompareGuid (&Event
->EventGroup
, EventGroup
)) {
252 CoreNotifyEvent (Event
);
256 CoreReleaseEventLock ();
262 @param Type The type of event to create and its mode and
264 @param NotifyTpl The task priority level of event notifications
265 @param NotifyFunction Pointer to the events notification function
266 @param NotifyContext Pointer to the notification functions context;
267 corresponds to parameter "Context" in the
268 notification function
269 @param Event Pointer to the newly created event if the call
270 succeeds; undefined otherwise
272 @retval EFI_SUCCESS The event structure was created
273 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
274 @retval EFI_OUT_OF_RESOURCES The event could not be allocated
281 IN EFI_TPL NotifyTpl
,
282 IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL
,
283 IN VOID
*NotifyContext OPTIONAL
,
287 return CoreCreateEventEx (Type
, NotifyTpl
, NotifyFunction
, NotifyContext
, NULL
, Event
);
291 Creates an event in a group.
293 @param Type The type of event to create and its mode and
295 @param NotifyTpl The task priority level of event notifications
296 @param NotifyFunction Pointer to the events notification function
297 @param NotifyContext Pointer to the notification functions context;
298 corresponds to parameter "Context" in the
299 notification function
300 @param EventGroup GUID for EventGroup if NULL act the same as
302 @param Event Pointer to the newly created event if the call
303 succeeds; undefined otherwise
305 @retval EFI_SUCCESS The event structure was created
306 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
307 @retval EFI_OUT_OF_RESOURCES The event could not be allocated
314 IN EFI_TPL NotifyTpl
,
315 IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL
,
316 IN CONST VOID
*NotifyContext OPTIONAL
,
317 IN CONST EFI_GUID
*EventGroup OPTIONAL
,
322 // If it's a notify type of event, check for invalid NotifyTpl
324 if ((Type
& (EVT_NOTIFY_WAIT
| EVT_NOTIFY_SIGNAL
)) != 0) {
325 if ((NotifyTpl
!= TPL_APPLICATION
) &&
326 (NotifyTpl
!= TPL_CALLBACK
) &&
327 (NotifyTpl
!= TPL_NOTIFY
))
329 return EFI_INVALID_PARAMETER
;
333 return CoreCreateEventInternal (Type
, NotifyTpl
, NotifyFunction
, NotifyContext
, EventGroup
, Event
);
337 Creates a general-purpose event structure
339 @param Type The type of event to create and its mode and
341 @param NotifyTpl The task priority level of event notifications
342 @param NotifyFunction Pointer to the events notification function
343 @param NotifyContext Pointer to the notification functions context;
344 corresponds to parameter "Context" in the
345 notification function
346 @param EventGroup GUID for EventGroup if NULL act the same as
348 @param Event Pointer to the newly created event if the call
349 succeeds; undefined otherwise
351 @retval EFI_SUCCESS The event structure was created
352 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
353 @retval EFI_OUT_OF_RESOURCES The event could not be allocated
358 CoreCreateEventInternal (
360 IN EFI_TPL NotifyTpl
,
361 IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL
,
362 IN CONST VOID
*NotifyContext OPTIONAL
,
363 IN CONST EFI_GUID
*EventGroup OPTIONAL
,
372 return EFI_INVALID_PARAMETER
;
376 // Check to make sure no reserved flags are set
378 Status
= EFI_INVALID_PARAMETER
;
379 for (Index
= 0; Index
< (sizeof (mEventTable
) / sizeof (UINT32
)); Index
++) {
380 if (Type
== mEventTable
[Index
]) {
381 Status
= EFI_SUCCESS
;
386 if (EFI_ERROR (Status
)) {
387 return EFI_INVALID_PARAMETER
;
391 // Convert Event type for pre-defined Event groups
393 if (EventGroup
!= NULL
) {
395 // For event group, type EVT_SIGNAL_EXIT_BOOT_SERVICES and EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
398 if ((Type
== EVT_SIGNAL_EXIT_BOOT_SERVICES
) || (Type
== EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
)) {
399 return EFI_INVALID_PARAMETER
;
402 if (CompareGuid (EventGroup
, &gEfiEventExitBootServicesGuid
)) {
403 Type
= EVT_SIGNAL_EXIT_BOOT_SERVICES
;
404 } else if (CompareGuid (EventGroup
, &gEfiEventVirtualAddressChangeGuid
)) {
405 Type
= EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
;
409 // Convert EFI 1.10 Events to their UEFI 2.0 CreateEventEx mapping
411 if (Type
== EVT_SIGNAL_EXIT_BOOT_SERVICES
) {
412 EventGroup
= &gEfiEventExitBootServicesGuid
;
413 } else if (Type
== EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
) {
414 EventGroup
= &gEfiEventVirtualAddressChangeGuid
;
419 // If it's a notify type of event, check its parameters
421 if ((Type
& (EVT_NOTIFY_WAIT
| EVT_NOTIFY_SIGNAL
)) != 0) {
423 // Check for an invalid NotifyFunction or NotifyTpl
425 if ((NotifyFunction
== NULL
) ||
426 (NotifyTpl
<= TPL_APPLICATION
) ||
427 (NotifyTpl
>= TPL_HIGH_LEVEL
))
429 return EFI_INVALID_PARAMETER
;
433 // No notification needed, zero ignored values
436 NotifyFunction
= NULL
;
437 NotifyContext
= NULL
;
441 // Allocate and initialize a new event structure.
443 if ((Type
& EVT_RUNTIME
) != 0) {
444 IEvent
= AllocateRuntimeZeroPool (sizeof (IEVENT
));
446 IEvent
= AllocateZeroPool (sizeof (IEVENT
));
449 if (IEvent
== NULL
) {
450 return EFI_OUT_OF_RESOURCES
;
453 IEvent
->Signature
= EVENT_SIGNATURE
;
456 IEvent
->NotifyTpl
= NotifyTpl
;
457 IEvent
->NotifyFunction
= NotifyFunction
;
458 IEvent
->NotifyContext
= (VOID
*)NotifyContext
;
459 if (EventGroup
!= NULL
) {
460 CopyGuid (&IEvent
->EventGroup
, EventGroup
);
461 IEvent
->ExFlag
|= EVT_EXFLAG_EVENT_GROUP
;
466 if ((Type
& EVT_RUNTIME
) != 0) {
468 // Keep a list of all RT events so we can tell the RT AP.
470 IEvent
->RuntimeData
.Type
= Type
;
471 IEvent
->RuntimeData
.NotifyTpl
= NotifyTpl
;
472 IEvent
->RuntimeData
.NotifyFunction
= NotifyFunction
;
473 IEvent
->RuntimeData
.NotifyContext
= (VOID
*)NotifyContext
;
475 // Work around the bug in the Platform Init specification (v1.7), reported
476 // as Mantis#2017: "EFI_RUNTIME_EVENT_ENTRY.Event" should have type
477 // EFI_EVENT, not (EFI_EVENT*). The PI spec documents the field correctly
478 // as "The EFI_EVENT returned by CreateEvent()", but the type of the field
479 // doesn't match the natural language description. Therefore we need an
480 // explicit cast here.
482 IEvent
->RuntimeData
.Event
= (EFI_EVENT
*)IEvent
;
483 InsertTailList (&gRuntime
->EventHead
, &IEvent
->RuntimeData
.Link
);
486 CoreAcquireEventLock ();
488 if ((Type
& EVT_NOTIFY_SIGNAL
) != 0x00000000) {
490 // The Event's NotifyFunction must be queued whenever the event is signaled
492 InsertHeadList (&gEventSignalQueue
, &IEvent
->SignalLink
);
495 CoreReleaseEventLock ();
504 Signals the event. Queues the event to be notified if needed.
506 @param UserEvent The event to signal .
508 @retval EFI_INVALID_PARAMETER Parameters are not valid.
509 @retval EFI_SUCCESS The event was signaled.
515 IN EFI_EVENT UserEvent
523 return EFI_INVALID_PARAMETER
;
526 if (Event
->Signature
!= EVENT_SIGNATURE
) {
527 return EFI_INVALID_PARAMETER
;
530 CoreAcquireEventLock ();
533 // If the event is not already signalled, do so
536 if (Event
->SignalCount
== 0x00000000) {
537 Event
->SignalCount
++;
540 // If signalling type is a notify function, queue it
542 if ((Event
->Type
& EVT_NOTIFY_SIGNAL
) != 0) {
543 if ((Event
->ExFlag
& EVT_EXFLAG_EVENT_GROUP
) != 0) {
545 // The CreateEventEx() style requires all members of the Event Group
548 CoreReleaseEventLock ();
549 CoreNotifySignalList (&Event
->EventGroup
);
550 CoreAcquireEventLock ();
552 CoreNotifyEvent (Event
);
557 CoreReleaseEventLock ();
562 Check the status of an event.
564 @param UserEvent The event to check
566 @retval EFI_SUCCESS The event is in the signaled state
567 @retval EFI_NOT_READY The event is not in the signaled state
568 @retval EFI_INVALID_PARAMETER Event is of type EVT_NOTIFY_SIGNAL
574 IN EFI_EVENT UserEvent
583 return EFI_INVALID_PARAMETER
;
586 if (Event
->Signature
!= EVENT_SIGNATURE
) {
587 return EFI_INVALID_PARAMETER
;
590 if ((Event
->Type
& EVT_NOTIFY_SIGNAL
) != 0) {
591 return EFI_INVALID_PARAMETER
;
594 Status
= EFI_NOT_READY
;
596 if ((Event
->SignalCount
== 0) && ((Event
->Type
& EVT_NOTIFY_WAIT
) != 0)) {
598 // Queue the wait notify function
600 CoreAcquireEventLock ();
601 if (Event
->SignalCount
== 0) {
602 CoreNotifyEvent (Event
);
605 CoreReleaseEventLock ();
609 // If the even looks signalled, get the lock and clear it
612 if (Event
->SignalCount
!= 0) {
613 CoreAcquireEventLock ();
615 if (Event
->SignalCount
!= 0) {
616 Event
->SignalCount
= 0;
617 Status
= EFI_SUCCESS
;
620 CoreReleaseEventLock ();
627 Stops execution until an event is signaled.
629 @param NumberOfEvents The number of events in the UserEvents array
630 @param UserEvents An array of EFI_EVENT
631 @param UserIndex Pointer to the index of the event which
632 satisfied the wait condition
634 @retval EFI_SUCCESS The event indicated by Index was signaled.
635 @retval EFI_INVALID_PARAMETER The event indicated by Index has a notification
636 function or Event was not a valid type
637 @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION
643 IN UINTN NumberOfEvents
,
644 IN EFI_EVENT
*UserEvents
,
652 // Can only WaitForEvent at TPL_APPLICATION
654 if (gEfiCurrentTpl
!= TPL_APPLICATION
) {
655 return EFI_UNSUPPORTED
;
658 if (NumberOfEvents
== 0) {
659 return EFI_INVALID_PARAMETER
;
662 if (UserEvents
== NULL
) {
663 return EFI_INVALID_PARAMETER
;
667 for (Index
= 0; Index
< NumberOfEvents
; Index
++) {
668 Status
= CoreCheckEvent (UserEvents
[Index
]);
671 // provide index of event that caused problem
673 if (Status
!= EFI_NOT_READY
) {
674 if (UserIndex
!= NULL
) {
683 // Signal the Idle event
685 CoreSignalEvent (gIdleLoopEvent
);
690 Closes an event and frees the event structure.
692 @param UserEvent Event to close
694 @retval EFI_INVALID_PARAMETER Parameters are not valid.
695 @retval EFI_SUCCESS The event has been closed
701 IN EFI_EVENT UserEvent
710 return EFI_INVALID_PARAMETER
;
713 if (Event
->Signature
!= EVENT_SIGNATURE
) {
714 return EFI_INVALID_PARAMETER
;
718 // If it's a timer event, make sure it's not pending
720 if ((Event
->Type
& EVT_TIMER
) != 0) {
721 CoreSetTimer (Event
, TimerCancel
, 0);
724 CoreAcquireEventLock ();
727 // If the event is queued somewhere, remove it
730 if (Event
->RuntimeData
.Link
.ForwardLink
!= NULL
) {
731 RemoveEntryList (&Event
->RuntimeData
.Link
);
734 if (Event
->NotifyLink
.ForwardLink
!= NULL
) {
735 RemoveEntryList (&Event
->NotifyLink
);
738 if (Event
->SignalLink
.ForwardLink
!= NULL
) {
739 RemoveEntryList (&Event
->SignalLink
);
742 CoreReleaseEventLock ();
745 // If the event is registered on a protocol notify, then remove it from the protocol database
747 if ((Event
->ExFlag
& EVT_EXFLAG_EVENT_PROTOCOL_NOTIFICATION
) != 0) {
748 CoreUnregisterProtocolNotify (Event
);
752 // To avoid the Event to be signalled wrongly after closed,
753 // clear the Signature of Event before free pool.
755 Event
->Signature
= 0;
756 Status
= CoreFreePool (Event
);
757 ASSERT_EFI_ERROR (Status
);