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
15 /// gEfiCurrentTpl - Current Task priority level
17 EFI_TPL gEfiCurrentTpl
= TPL_APPLICATION
;
20 /// gEventQueueLock - Protects the event queues
22 EFI_LOCK gEventQueueLock
= EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL
);
25 /// gEventQueue - A list of event's to notify for each priority level
27 LIST_ENTRY gEventQueue
[TPL_HIGH_LEVEL
+ 1];
30 /// gEventPending - A bitmask of the EventQueues that are pending
32 UINTN gEventPending
= 0;
35 /// gEventSignalQueue - A list of events to signal based on EventGroup type
37 LIST_ENTRY gEventSignalQueue
= INITIALIZE_LIST_HEAD_VARIABLE (gEventSignalQueue
);
40 /// Enumerate the valid types
42 UINT32 mEventTable
[] = {
44 /// 0x80000200 Timer event with a notification function that is
45 /// queue when the event is signaled with SignalEvent()
47 EVT_TIMER
| EVT_NOTIFY_SIGNAL
,
49 /// 0x80000000 Timer event without a notification function. It can be
50 /// signaled with SignalEvent() and checked with CheckEvent() or WaitForEvent().
54 /// 0x00000100 Generic event with a notification function that
55 /// can be waited on with CheckEvent() or WaitForEvent()
59 /// 0x00000200 Generic event with a notification function that
60 /// is queue when the event is signaled with SignalEvent()
64 /// 0x00000201 ExitBootServicesEvent.
66 EVT_SIGNAL_EXIT_BOOT_SERVICES
,
68 /// 0x60000202 SetVirtualAddressMapEvent.
70 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
,
73 /// 0x00000000 Generic event without a notification function.
74 /// It can be signaled with SignalEvent() and checked with CheckEvent()
75 /// or WaitForEvent().
79 /// 0x80000100 Timer event with a notification function that can be
80 /// waited on with CheckEvent() or WaitForEvent()
82 EVT_TIMER
| EVT_NOTIFY_WAIT
,
86 /// gIdleLoopEvent - Event which is signalled when the core is idle
88 EFI_EVENT gIdleLoopEvent
= NULL
;
92 Enter critical section by acquiring the lock on gEventQueueLock.
96 CoreAcquireEventLock (
100 CoreAcquireLock (&gEventQueueLock
);
105 Exit critical section by releasing the lock on gEventQueueLock.
109 CoreReleaseEventLock (
113 CoreReleaseLock (&gEventQueueLock
);
119 Initializes "event" support.
121 @retval EFI_SUCCESS Always return success
125 CoreInitializeEventServices (
131 for (Index
=0; Index
<= TPL_HIGH_LEVEL
; Index
++) {
132 InitializeListHead (&gEventQueue
[Index
]);
135 CoreInitializeTimer ();
140 EfiEventEmptyFunction
,
152 Dispatches all pending events.
154 @param Priority The task priority level of event notifications
159 CoreDispatchEventNotifies (
166 CoreAcquireEventLock ();
167 ASSERT (gEventQueueLock
.OwnerTpl
== Priority
);
168 Head
= &gEventQueue
[Priority
];
171 // Dispatch all the pending notifications
173 while (!IsListEmpty (Head
)) {
175 Event
= CR (Head
->ForwardLink
, IEVENT
, NotifyLink
, EVENT_SIGNATURE
);
176 RemoveEntryList (&Event
->NotifyLink
);
178 Event
->NotifyLink
.ForwardLink
= NULL
;
181 // Only clear the SIGNAL status if it is a SIGNAL type event.
182 // WAIT type events are only cleared in CheckEvent()
184 if ((Event
->Type
& EVT_NOTIFY_SIGNAL
) != 0) {
185 Event
->SignalCount
= 0;
188 CoreReleaseEventLock ();
193 ASSERT (Event
->NotifyFunction
!= NULL
);
194 Event
->NotifyFunction (Event
, Event
->NotifyContext
);
197 // Check for next pending event
199 CoreAcquireEventLock ();
202 gEventPending
&= ~(UINTN
)(1 << Priority
);
203 CoreReleaseEventLock ();
209 Queues the event's notification function to fire.
211 @param Event The Event to notify
221 // Event database must be locked
223 ASSERT_LOCKED (&gEventQueueLock
);
226 // If the event is queued somewhere, remove it
229 if (Event
->NotifyLink
.ForwardLink
!= NULL
) {
230 RemoveEntryList (&Event
->NotifyLink
);
231 Event
->NotifyLink
.ForwardLink
= NULL
;
235 // Queue the event to the pending notification list
238 InsertTailList (&gEventQueue
[Event
->NotifyTpl
], &Event
->NotifyLink
);
239 gEventPending
|= (UINTN
)(1 << Event
->NotifyTpl
);
246 Signals all events in the EventGroup.
248 @param EventGroup The list to signal
252 CoreNotifySignalList (
253 IN EFI_GUID
*EventGroup
260 CoreAcquireEventLock ();
262 Head
= &gEventSignalQueue
;
263 for (Link
= Head
->ForwardLink
; Link
!= Head
; Link
= Link
->ForwardLink
) {
264 Event
= CR (Link
, IEVENT
, SignalLink
, EVENT_SIGNATURE
);
265 if (CompareGuid (&Event
->EventGroup
, EventGroup
)) {
266 CoreNotifyEvent (Event
);
270 CoreReleaseEventLock ();
277 @param Type The type of event to create and its mode and
279 @param NotifyTpl The task priority level of event notifications
280 @param NotifyFunction Pointer to the events notification function
281 @param NotifyContext Pointer to the notification functions context;
282 corresponds to parameter "Context" in the
283 notification function
284 @param Event Pointer to the newly created event if the call
285 succeeds; undefined otherwise
287 @retval EFI_SUCCESS The event structure was created
288 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
289 @retval EFI_OUT_OF_RESOURCES The event could not be allocated
296 IN EFI_TPL NotifyTpl
,
297 IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL
,
298 IN VOID
*NotifyContext OPTIONAL
,
302 return CoreCreateEventEx (Type
, NotifyTpl
, NotifyFunction
, NotifyContext
, NULL
, Event
);
308 Creates an event in a group.
310 @param Type The type of event to create and its mode and
312 @param NotifyTpl The task priority level of event notifications
313 @param NotifyFunction Pointer to the events notification function
314 @param NotifyContext Pointer to the notification functions context;
315 corresponds to parameter "Context" in the
316 notification function
317 @param EventGroup GUID for EventGroup if NULL act the same as
319 @param Event Pointer to the newly created event if the call
320 succeeds; undefined otherwise
322 @retval EFI_SUCCESS The event structure was created
323 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
324 @retval EFI_OUT_OF_RESOURCES The event could not be allocated
331 IN EFI_TPL NotifyTpl
,
332 IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL
,
333 IN CONST VOID
*NotifyContext OPTIONAL
,
334 IN CONST EFI_GUID
*EventGroup OPTIONAL
,
339 // If it's a notify type of event, check for invalid NotifyTpl
341 if ((Type
& (EVT_NOTIFY_WAIT
| EVT_NOTIFY_SIGNAL
)) != 0) {
342 if (NotifyTpl
!= TPL_APPLICATION
&&
343 NotifyTpl
!= TPL_CALLBACK
&&
344 NotifyTpl
!= TPL_NOTIFY
) {
345 return EFI_INVALID_PARAMETER
;
349 return CoreCreateEventInternal (Type
, NotifyTpl
, NotifyFunction
, NotifyContext
, EventGroup
, Event
);
353 Creates a general-purpose event structure
355 @param Type The type of event to create and its mode and
357 @param NotifyTpl The task priority level of event notifications
358 @param NotifyFunction Pointer to the events notification function
359 @param NotifyContext Pointer to the notification functions context;
360 corresponds to parameter "Context" in the
361 notification function
362 @param EventGroup GUID for EventGroup if NULL act the same as
364 @param Event Pointer to the newly created event if the call
365 succeeds; undefined otherwise
367 @retval EFI_SUCCESS The event structure was created
368 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
369 @retval EFI_OUT_OF_RESOURCES The event could not be allocated
374 CoreCreateEventInternal (
376 IN EFI_TPL NotifyTpl
,
377 IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL
,
378 IN CONST VOID
*NotifyContext OPTIONAL
,
379 IN CONST EFI_GUID
*EventGroup OPTIONAL
,
389 return EFI_INVALID_PARAMETER
;
393 // Check to make sure no reserved flags are set
395 Status
= EFI_INVALID_PARAMETER
;
396 for (Index
= 0; Index
< (sizeof (mEventTable
) / sizeof (UINT32
)); Index
++) {
397 if (Type
== mEventTable
[Index
]) {
398 Status
= EFI_SUCCESS
;
402 if(EFI_ERROR (Status
)) {
403 return EFI_INVALID_PARAMETER
;
407 // Convert Event type for pre-defined Event groups
409 if (EventGroup
!= NULL
) {
411 // For event group, type EVT_SIGNAL_EXIT_BOOT_SERVICES and EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
414 if ((Type
== EVT_SIGNAL_EXIT_BOOT_SERVICES
) || (Type
== EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
)) {
415 return EFI_INVALID_PARAMETER
;
417 if (CompareGuid (EventGroup
, &gEfiEventExitBootServicesGuid
)) {
418 Type
= EVT_SIGNAL_EXIT_BOOT_SERVICES
;
419 } else if (CompareGuid (EventGroup
, &gEfiEventVirtualAddressChangeGuid
)) {
420 Type
= EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
;
424 // Convert EFI 1.10 Events to their UEFI 2.0 CreateEventEx mapping
426 if (Type
== EVT_SIGNAL_EXIT_BOOT_SERVICES
) {
427 EventGroup
= &gEfiEventExitBootServicesGuid
;
428 } else if (Type
== EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
) {
429 EventGroup
= &gEfiEventVirtualAddressChangeGuid
;
434 // If it's a notify type of event, check its parameters
436 if ((Type
& (EVT_NOTIFY_WAIT
| EVT_NOTIFY_SIGNAL
)) != 0) {
438 // Check for an invalid NotifyFunction or NotifyTpl
440 if ((NotifyFunction
== NULL
) ||
441 (NotifyTpl
<= TPL_APPLICATION
) ||
442 (NotifyTpl
>= TPL_HIGH_LEVEL
)) {
443 return EFI_INVALID_PARAMETER
;
448 // No notification needed, zero ignored values
451 NotifyFunction
= NULL
;
452 NotifyContext
= NULL
;
456 // Allocate and initialize a new event structure.
458 if ((Type
& EVT_RUNTIME
) != 0) {
459 IEvent
= AllocateRuntimeZeroPool (sizeof (IEVENT
));
461 IEvent
= AllocateZeroPool (sizeof (IEVENT
));
463 if (IEvent
== NULL
) {
464 return EFI_OUT_OF_RESOURCES
;
467 IEvent
->Signature
= EVENT_SIGNATURE
;
470 IEvent
->NotifyTpl
= NotifyTpl
;
471 IEvent
->NotifyFunction
= NotifyFunction
;
472 IEvent
->NotifyContext
= (VOID
*)NotifyContext
;
473 if (EventGroup
!= NULL
) {
474 CopyGuid (&IEvent
->EventGroup
, EventGroup
);
475 IEvent
->ExFlag
|= EVT_EXFLAG_EVENT_GROUP
;
480 if ((Type
& EVT_RUNTIME
) != 0) {
482 // Keep a list of all RT events so we can tell the RT AP.
484 IEvent
->RuntimeData
.Type
= Type
;
485 IEvent
->RuntimeData
.NotifyTpl
= NotifyTpl
;
486 IEvent
->RuntimeData
.NotifyFunction
= NotifyFunction
;
487 IEvent
->RuntimeData
.NotifyContext
= (VOID
*) NotifyContext
;
489 // Work around the bug in the Platform Init specification (v1.7), reported
490 // as Mantis#2017: "EFI_RUNTIME_EVENT_ENTRY.Event" should have type
491 // EFI_EVENT, not (EFI_EVENT*). The PI spec documents the field correctly
492 // as "The EFI_EVENT returned by CreateEvent()", but the type of the field
493 // doesn't match the natural language description. Therefore we need an
494 // explicit cast here.
496 IEvent
->RuntimeData
.Event
= (EFI_EVENT
*) IEvent
;
497 InsertTailList (&gRuntime
->EventHead
, &IEvent
->RuntimeData
.Link
);
500 CoreAcquireEventLock ();
502 if ((Type
& EVT_NOTIFY_SIGNAL
) != 0x00000000) {
504 // The Event's NotifyFunction must be queued whenever the event is signaled
506 InsertHeadList (&gEventSignalQueue
, &IEvent
->SignalLink
);
509 CoreReleaseEventLock ();
521 Signals the event. Queues the event to be notified if needed.
523 @param UserEvent The event to signal .
525 @retval EFI_INVALID_PARAMETER Parameters are not valid.
526 @retval EFI_SUCCESS The event was signaled.
532 IN EFI_EVENT UserEvent
540 return EFI_INVALID_PARAMETER
;
543 if (Event
->Signature
!= EVENT_SIGNATURE
) {
544 return EFI_INVALID_PARAMETER
;
547 CoreAcquireEventLock ();
550 // If the event is not already signalled, do so
553 if (Event
->SignalCount
== 0x00000000) {
554 Event
->SignalCount
++;
557 // If signalling type is a notify function, queue it
559 if ((Event
->Type
& EVT_NOTIFY_SIGNAL
) != 0) {
560 if ((Event
->ExFlag
& EVT_EXFLAG_EVENT_GROUP
) != 0) {
562 // The CreateEventEx() style requires all members of the Event Group
565 CoreReleaseEventLock ();
566 CoreNotifySignalList (&Event
->EventGroup
);
567 CoreAcquireEventLock ();
569 CoreNotifyEvent (Event
);
574 CoreReleaseEventLock ();
581 Check the status of an event.
583 @param UserEvent The event to check
585 @retval EFI_SUCCESS The event is in the signaled state
586 @retval EFI_NOT_READY The event is not in the signaled state
587 @retval EFI_INVALID_PARAMETER Event is of type EVT_NOTIFY_SIGNAL
593 IN EFI_EVENT UserEvent
602 return EFI_INVALID_PARAMETER
;
605 if (Event
->Signature
!= EVENT_SIGNATURE
) {
606 return EFI_INVALID_PARAMETER
;
609 if ((Event
->Type
& EVT_NOTIFY_SIGNAL
) != 0) {
610 return EFI_INVALID_PARAMETER
;
613 Status
= EFI_NOT_READY
;
615 if ((Event
->SignalCount
== 0) && ((Event
->Type
& EVT_NOTIFY_WAIT
) != 0)) {
618 // Queue the wait notify function
620 CoreAcquireEventLock ();
621 if (Event
->SignalCount
== 0) {
622 CoreNotifyEvent (Event
);
624 CoreReleaseEventLock ();
628 // If the even looks signalled, get the lock and clear it
631 if (Event
->SignalCount
!= 0) {
632 CoreAcquireEventLock ();
634 if (Event
->SignalCount
!= 0) {
635 Event
->SignalCount
= 0;
636 Status
= EFI_SUCCESS
;
639 CoreReleaseEventLock ();
648 Stops execution until an event is signaled.
650 @param NumberOfEvents The number of events in the UserEvents array
651 @param UserEvents An array of EFI_EVENT
652 @param UserIndex Pointer to the index of the event which
653 satisfied the wait condition
655 @retval EFI_SUCCESS The event indicated by Index was signaled.
656 @retval EFI_INVALID_PARAMETER The event indicated by Index has a notification
657 function or Event was not a valid type
658 @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION
664 IN UINTN NumberOfEvents
,
665 IN EFI_EVENT
*UserEvents
,
673 // Can only WaitForEvent at TPL_APPLICATION
675 if (gEfiCurrentTpl
!= TPL_APPLICATION
) {
676 return EFI_UNSUPPORTED
;
679 if (NumberOfEvents
== 0) {
680 return EFI_INVALID_PARAMETER
;
683 if (UserEvents
== NULL
) {
684 return EFI_INVALID_PARAMETER
;
689 for(Index
= 0; Index
< NumberOfEvents
; Index
++) {
691 Status
= CoreCheckEvent (UserEvents
[Index
]);
694 // provide index of event that caused problem
696 if (Status
!= EFI_NOT_READY
) {
697 if (UserIndex
!= NULL
) {
705 // Signal the Idle event
707 CoreSignalEvent (gIdleLoopEvent
);
713 Closes an event and frees the event structure.
715 @param UserEvent Event to close
717 @retval EFI_INVALID_PARAMETER Parameters are not valid.
718 @retval EFI_SUCCESS The event has been closed
724 IN EFI_EVENT UserEvent
733 return EFI_INVALID_PARAMETER
;
736 if (Event
->Signature
!= EVENT_SIGNATURE
) {
737 return EFI_INVALID_PARAMETER
;
741 // If it's a timer event, make sure it's not pending
743 if ((Event
->Type
& EVT_TIMER
) != 0) {
744 CoreSetTimer (Event
, TimerCancel
, 0);
747 CoreAcquireEventLock ();
750 // If the event is queued somewhere, remove it
753 if (Event
->RuntimeData
.Link
.ForwardLink
!= NULL
) {
754 RemoveEntryList (&Event
->RuntimeData
.Link
);
757 if (Event
->NotifyLink
.ForwardLink
!= NULL
) {
758 RemoveEntryList (&Event
->NotifyLink
);
761 if (Event
->SignalLink
.ForwardLink
!= NULL
) {
762 RemoveEntryList (&Event
->SignalLink
);
765 CoreReleaseEventLock ();
768 // If the event is registered on a protocol notify, then remove it from the protocol database
770 if ((Event
->ExFlag
& EVT_EXFLAG_EVENT_PROTOCOL_NOTIFICATION
) != 0) {
771 CoreUnregisterProtocolNotify (Event
);
775 // To avoid the Event to be signalled wrongly after closed,
776 // clear the Signature of Event before free pool.
778 Event
->Signature
= 0;
779 Status
= CoreFreePool (Event
);
780 ASSERT_EFI_ERROR (Status
);