]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/Event/Event.c
MdeModulePkg: Change OPTIONAL keyword usage style
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Event / Event.c
1 /** @file
2 UEFI Event support functions implemented in this file.
3
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
7
8 **/
9
10
11 #include "DxeMain.h"
12 #include "Event.h"
13
14 ///
15 /// gEfiCurrentTpl - Current Task priority level
16 ///
17 EFI_TPL gEfiCurrentTpl = TPL_APPLICATION;
18
19 ///
20 /// gEventQueueLock - Protects the event queues
21 ///
22 EFI_LOCK gEventQueueLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL);
23
24 ///
25 /// gEventQueue - A list of event's to notify for each priority level
26 ///
27 LIST_ENTRY gEventQueue[TPL_HIGH_LEVEL + 1];
28
29 ///
30 /// gEventPending - A bitmask of the EventQueues that are pending
31 ///
32 UINTN gEventPending = 0;
33
34 ///
35 /// gEventSignalQueue - A list of events to signal based on EventGroup type
36 ///
37 LIST_ENTRY gEventSignalQueue = INITIALIZE_LIST_HEAD_VARIABLE (gEventSignalQueue);
38
39 ///
40 /// Enumerate the valid types
41 ///
42 UINT32 mEventTable[] = {
43 ///
44 /// 0x80000200 Timer event with a notification function that is
45 /// queue when the event is signaled with SignalEvent()
46 ///
47 EVT_TIMER | EVT_NOTIFY_SIGNAL,
48 ///
49 /// 0x80000000 Timer event without a notification function. It can be
50 /// signaled with SignalEvent() and checked with CheckEvent() or WaitForEvent().
51 ///
52 EVT_TIMER,
53 ///
54 /// 0x00000100 Generic event with a notification function that
55 /// can be waited on with CheckEvent() or WaitForEvent()
56 ///
57 EVT_NOTIFY_WAIT,
58 ///
59 /// 0x00000200 Generic event with a notification function that
60 /// is queue when the event is signaled with SignalEvent()
61 ///
62 EVT_NOTIFY_SIGNAL,
63 ///
64 /// 0x00000201 ExitBootServicesEvent.
65 ///
66 EVT_SIGNAL_EXIT_BOOT_SERVICES,
67 ///
68 /// 0x60000202 SetVirtualAddressMapEvent.
69 ///
70 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
71
72 ///
73 /// 0x00000000 Generic event without a notification function.
74 /// It can be signaled with SignalEvent() and checked with CheckEvent()
75 /// or WaitForEvent().
76 ///
77 0x00000000,
78 ///
79 /// 0x80000100 Timer event with a notification function that can be
80 /// waited on with CheckEvent() or WaitForEvent()
81 ///
82 EVT_TIMER | EVT_NOTIFY_WAIT,
83 };
84
85 ///
86 /// gIdleLoopEvent - Event which is signalled when the core is idle
87 ///
88 EFI_EVENT gIdleLoopEvent = NULL;
89
90
91 /**
92 Enter critical section by acquiring the lock on gEventQueueLock.
93
94 **/
95 VOID
96 CoreAcquireEventLock (
97 VOID
98 )
99 {
100 CoreAcquireLock (&gEventQueueLock);
101 }
102
103
104 /**
105 Exit critical section by releasing the lock on gEventQueueLock.
106
107 **/
108 VOID
109 CoreReleaseEventLock (
110 VOID
111 )
112 {
113 CoreReleaseLock (&gEventQueueLock);
114 }
115
116
117
118 /**
119 Initializes "event" support.
120
121 @retval EFI_SUCCESS Always return success
122
123 **/
124 EFI_STATUS
125 CoreInitializeEventServices (
126 VOID
127 )
128 {
129 UINTN Index;
130
131 for (Index=0; Index <= TPL_HIGH_LEVEL; Index++) {
132 InitializeListHead (&gEventQueue[Index]);
133 }
134
135 CoreInitializeTimer ();
136
137 CoreCreateEventEx (
138 EVT_NOTIFY_SIGNAL,
139 TPL_NOTIFY,
140 EfiEventEmptyFunction,
141 NULL,
142 &gIdleLoopEventGuid,
143 &gIdleLoopEvent
144 );
145
146 return EFI_SUCCESS;
147 }
148
149
150
151 /**
152 Dispatches all pending events.
153
154 @param Priority The task priority level of event notifications
155 to dispatch
156
157 **/
158 VOID
159 CoreDispatchEventNotifies (
160 IN EFI_TPL Priority
161 )
162 {
163 IEVENT *Event;
164 LIST_ENTRY *Head;
165
166 CoreAcquireEventLock ();
167 ASSERT (gEventQueueLock.OwnerTpl == Priority);
168 Head = &gEventQueue[Priority];
169
170 //
171 // Dispatch all the pending notifications
172 //
173 while (!IsListEmpty (Head)) {
174
175 Event = CR (Head->ForwardLink, IEVENT, NotifyLink, EVENT_SIGNATURE);
176 RemoveEntryList (&Event->NotifyLink);
177
178 Event->NotifyLink.ForwardLink = NULL;
179
180 //
181 // Only clear the SIGNAL status if it is a SIGNAL type event.
182 // WAIT type events are only cleared in CheckEvent()
183 //
184 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
185 Event->SignalCount = 0;
186 }
187
188 CoreReleaseEventLock ();
189
190 //
191 // Notify this event
192 //
193 ASSERT (Event->NotifyFunction != NULL);
194 Event->NotifyFunction (Event, Event->NotifyContext);
195
196 //
197 // Check for next pending event
198 //
199 CoreAcquireEventLock ();
200 }
201
202 gEventPending &= ~(UINTN)(1 << Priority);
203 CoreReleaseEventLock ();
204 }
205
206
207
208 /**
209 Queues the event's notification function to fire.
210
211 @param Event The Event to notify
212
213 **/
214 VOID
215 CoreNotifyEvent (
216 IN IEVENT *Event
217 )
218 {
219
220 //
221 // Event database must be locked
222 //
223 ASSERT_LOCKED (&gEventQueueLock);
224
225 //
226 // If the event is queued somewhere, remove it
227 //
228
229 if (Event->NotifyLink.ForwardLink != NULL) {
230 RemoveEntryList (&Event->NotifyLink);
231 Event->NotifyLink.ForwardLink = NULL;
232 }
233
234 //
235 // Queue the event to the pending notification list
236 //
237
238 InsertTailList (&gEventQueue[Event->NotifyTpl], &Event->NotifyLink);
239 gEventPending |= (UINTN)(1 << Event->NotifyTpl);
240 }
241
242
243
244
245 /**
246 Signals all events in the EventGroup.
247
248 @param EventGroup The list to signal
249
250 **/
251 VOID
252 CoreNotifySignalList (
253 IN EFI_GUID *EventGroup
254 )
255 {
256 LIST_ENTRY *Link;
257 LIST_ENTRY *Head;
258 IEVENT *Event;
259
260 CoreAcquireEventLock ();
261
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);
267 }
268 }
269
270 CoreReleaseEventLock ();
271 }
272
273
274 /**
275 Creates an event.
276
277 @param Type The type of event to create and its mode and
278 attributes
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
286
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
290
291 **/
292 EFI_STATUS
293 EFIAPI
294 CoreCreateEvent (
295 IN UINT32 Type,
296 IN EFI_TPL NotifyTpl,
297 IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL,
298 IN VOID *NotifyContext OPTIONAL,
299 OUT EFI_EVENT *Event
300 )
301 {
302 return CoreCreateEventEx (Type, NotifyTpl, NotifyFunction, NotifyContext, NULL, Event);
303 }
304
305
306
307 /**
308 Creates an event in a group.
309
310 @param Type The type of event to create and its mode and
311 attributes
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
318 gBS->CreateEvent().
319 @param Event Pointer to the newly created event if the call
320 succeeds; undefined otherwise
321
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
325
326 **/
327 EFI_STATUS
328 EFIAPI
329 CoreCreateEventEx (
330 IN UINT32 Type,
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,
335 OUT EFI_EVENT *Event
336 )
337 {
338 //
339 // If it's a notify type of event, check for invalid NotifyTpl
340 //
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;
346 }
347 }
348
349 return CoreCreateEventInternal (Type, NotifyTpl, NotifyFunction, NotifyContext, EventGroup, Event);
350 }
351
352 /**
353 Creates a general-purpose event structure
354
355 @param Type The type of event to create and its mode and
356 attributes
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
363 gBS->CreateEvent().
364 @param Event Pointer to the newly created event if the call
365 succeeds; undefined otherwise
366
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
370
371 **/
372 EFI_STATUS
373 EFIAPI
374 CoreCreateEventInternal (
375 IN UINT32 Type,
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,
380 OUT EFI_EVENT *Event
381 )
382 {
383 EFI_STATUS Status;
384 IEVENT *IEvent;
385 INTN Index;
386
387
388 if (Event == NULL) {
389 return EFI_INVALID_PARAMETER;
390 }
391
392 //
393 // Check to make sure no reserved flags are set
394 //
395 Status = EFI_INVALID_PARAMETER;
396 for (Index = 0; Index < (sizeof (mEventTable) / sizeof (UINT32)); Index++) {
397 if (Type == mEventTable[Index]) {
398 Status = EFI_SUCCESS;
399 break;
400 }
401 }
402 if(EFI_ERROR (Status)) {
403 return EFI_INVALID_PARAMETER;
404 }
405
406 //
407 // Convert Event type for pre-defined Event groups
408 //
409 if (EventGroup != NULL) {
410 //
411 // For event group, type EVT_SIGNAL_EXIT_BOOT_SERVICES and EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
412 // are not valid
413 //
414 if ((Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) || (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)) {
415 return EFI_INVALID_PARAMETER;
416 }
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;
421 }
422 } else {
423 //
424 // Convert EFI 1.10 Events to their UEFI 2.0 CreateEventEx mapping
425 //
426 if (Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) {
427 EventGroup = &gEfiEventExitBootServicesGuid;
428 } else if (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) {
429 EventGroup = &gEfiEventVirtualAddressChangeGuid;
430 }
431 }
432
433 //
434 // If it's a notify type of event, check its parameters
435 //
436 if ((Type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) != 0) {
437 //
438 // Check for an invalid NotifyFunction or NotifyTpl
439 //
440 if ((NotifyFunction == NULL) ||
441 (NotifyTpl <= TPL_APPLICATION) ||
442 (NotifyTpl >= TPL_HIGH_LEVEL)) {
443 return EFI_INVALID_PARAMETER;
444 }
445
446 } else {
447 //
448 // No notification needed, zero ignored values
449 //
450 NotifyTpl = 0;
451 NotifyFunction = NULL;
452 NotifyContext = NULL;
453 }
454
455 //
456 // Allocate and initialize a new event structure.
457 //
458 if ((Type & EVT_RUNTIME) != 0) {
459 IEvent = AllocateRuntimeZeroPool (sizeof (IEVENT));
460 } else {
461 IEvent = AllocateZeroPool (sizeof (IEVENT));
462 }
463 if (IEvent == NULL) {
464 return EFI_OUT_OF_RESOURCES;
465 }
466
467 IEvent->Signature = EVENT_SIGNATURE;
468 IEvent->Type = Type;
469
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;
476 }
477
478 *Event = IEvent;
479
480 if ((Type & EVT_RUNTIME) != 0) {
481 //
482 // Keep a list of all RT events so we can tell the RT AP.
483 //
484 IEvent->RuntimeData.Type = Type;
485 IEvent->RuntimeData.NotifyTpl = NotifyTpl;
486 IEvent->RuntimeData.NotifyFunction = NotifyFunction;
487 IEvent->RuntimeData.NotifyContext = (VOID *) NotifyContext;
488 //
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.
495 //
496 IEvent->RuntimeData.Event = (EFI_EVENT *) IEvent;
497 InsertTailList (&gRuntime->EventHead, &IEvent->RuntimeData.Link);
498 }
499
500 CoreAcquireEventLock ();
501
502 if ((Type & EVT_NOTIFY_SIGNAL) != 0x00000000) {
503 //
504 // The Event's NotifyFunction must be queued whenever the event is signaled
505 //
506 InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
507 }
508
509 CoreReleaseEventLock ();
510
511 //
512 // Done
513 //
514 return EFI_SUCCESS;
515 }
516
517
518
519
520 /**
521 Signals the event. Queues the event to be notified if needed.
522
523 @param UserEvent The event to signal .
524
525 @retval EFI_INVALID_PARAMETER Parameters are not valid.
526 @retval EFI_SUCCESS The event was signaled.
527
528 **/
529 EFI_STATUS
530 EFIAPI
531 CoreSignalEvent (
532 IN EFI_EVENT UserEvent
533 )
534 {
535 IEVENT *Event;
536
537 Event = UserEvent;
538
539 if (Event == NULL) {
540 return EFI_INVALID_PARAMETER;
541 }
542
543 if (Event->Signature != EVENT_SIGNATURE) {
544 return EFI_INVALID_PARAMETER;
545 }
546
547 CoreAcquireEventLock ();
548
549 //
550 // If the event is not already signalled, do so
551 //
552
553 if (Event->SignalCount == 0x00000000) {
554 Event->SignalCount++;
555
556 //
557 // If signalling type is a notify function, queue it
558 //
559 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
560 if ((Event->ExFlag & EVT_EXFLAG_EVENT_GROUP) != 0) {
561 //
562 // The CreateEventEx() style requires all members of the Event Group
563 // to be signaled.
564 //
565 CoreReleaseEventLock ();
566 CoreNotifySignalList (&Event->EventGroup);
567 CoreAcquireEventLock ();
568 } else {
569 CoreNotifyEvent (Event);
570 }
571 }
572 }
573
574 CoreReleaseEventLock ();
575 return EFI_SUCCESS;
576 }
577
578
579
580 /**
581 Check the status of an event.
582
583 @param UserEvent The event to check
584
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
588
589 **/
590 EFI_STATUS
591 EFIAPI
592 CoreCheckEvent (
593 IN EFI_EVENT UserEvent
594 )
595 {
596 IEVENT *Event;
597 EFI_STATUS Status;
598
599 Event = UserEvent;
600
601 if (Event == NULL) {
602 return EFI_INVALID_PARAMETER;
603 }
604
605 if (Event->Signature != EVENT_SIGNATURE) {
606 return EFI_INVALID_PARAMETER;
607 }
608
609 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
610 return EFI_INVALID_PARAMETER;
611 }
612
613 Status = EFI_NOT_READY;
614
615 if ((Event->SignalCount == 0) && ((Event->Type & EVT_NOTIFY_WAIT) != 0)) {
616
617 //
618 // Queue the wait notify function
619 //
620 CoreAcquireEventLock ();
621 if (Event->SignalCount == 0) {
622 CoreNotifyEvent (Event);
623 }
624 CoreReleaseEventLock ();
625 }
626
627 //
628 // If the even looks signalled, get the lock and clear it
629 //
630
631 if (Event->SignalCount != 0) {
632 CoreAcquireEventLock ();
633
634 if (Event->SignalCount != 0) {
635 Event->SignalCount = 0;
636 Status = EFI_SUCCESS;
637 }
638
639 CoreReleaseEventLock ();
640 }
641
642 return Status;
643 }
644
645
646
647 /**
648 Stops execution until an event is signaled.
649
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
654
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
659
660 **/
661 EFI_STATUS
662 EFIAPI
663 CoreWaitForEvent (
664 IN UINTN NumberOfEvents,
665 IN EFI_EVENT *UserEvents,
666 OUT UINTN *UserIndex
667 )
668 {
669 EFI_STATUS Status;
670 UINTN Index;
671
672 //
673 // Can only WaitForEvent at TPL_APPLICATION
674 //
675 if (gEfiCurrentTpl != TPL_APPLICATION) {
676 return EFI_UNSUPPORTED;
677 }
678
679 if (NumberOfEvents == 0) {
680 return EFI_INVALID_PARAMETER;
681 }
682
683 if (UserEvents == NULL) {
684 return EFI_INVALID_PARAMETER;
685 }
686
687 for(;;) {
688
689 for(Index = 0; Index < NumberOfEvents; Index++) {
690
691 Status = CoreCheckEvent (UserEvents[Index]);
692
693 //
694 // provide index of event that caused problem
695 //
696 if (Status != EFI_NOT_READY) {
697 if (UserIndex != NULL) {
698 *UserIndex = Index;
699 }
700 return Status;
701 }
702 }
703
704 //
705 // Signal the Idle event
706 //
707 CoreSignalEvent (gIdleLoopEvent);
708 }
709 }
710
711
712 /**
713 Closes an event and frees the event structure.
714
715 @param UserEvent Event to close
716
717 @retval EFI_INVALID_PARAMETER Parameters are not valid.
718 @retval EFI_SUCCESS The event has been closed
719
720 **/
721 EFI_STATUS
722 EFIAPI
723 CoreCloseEvent (
724 IN EFI_EVENT UserEvent
725 )
726 {
727 EFI_STATUS Status;
728 IEVENT *Event;
729
730 Event = UserEvent;
731
732 if (Event == NULL) {
733 return EFI_INVALID_PARAMETER;
734 }
735
736 if (Event->Signature != EVENT_SIGNATURE) {
737 return EFI_INVALID_PARAMETER;
738 }
739
740 //
741 // If it's a timer event, make sure it's not pending
742 //
743 if ((Event->Type & EVT_TIMER) != 0) {
744 CoreSetTimer (Event, TimerCancel, 0);
745 }
746
747 CoreAcquireEventLock ();
748
749 //
750 // If the event is queued somewhere, remove it
751 //
752
753 if (Event->RuntimeData.Link.ForwardLink != NULL) {
754 RemoveEntryList (&Event->RuntimeData.Link);
755 }
756
757 if (Event->NotifyLink.ForwardLink != NULL) {
758 RemoveEntryList (&Event->NotifyLink);
759 }
760
761 if (Event->SignalLink.ForwardLink != NULL) {
762 RemoveEntryList (&Event->SignalLink);
763 }
764
765 CoreReleaseEventLock ();
766
767 //
768 // If the event is registered on a protocol notify, then remove it from the protocol database
769 //
770 if ((Event->ExFlag & EVT_EXFLAG_EVENT_PROTOCOL_NOTIFICATION) != 0) {
771 CoreUnregisterProtocolNotify (Event);
772 }
773
774 //
775 // To avoid the Event to be signalled wrongly after closed,
776 // clear the Signature of Event before free pool.
777 //
778 Event->Signature = 0;
779 Status = CoreFreePool (Event);
780 ASSERT_EFI_ERROR (Status);
781
782 return Status;
783 }