]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/Event/Event.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[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 IEvent->RuntimeData.Event = (EFI_EVENT *) IEvent;
489 InsertTailList (&gRuntime->EventHead, &IEvent->RuntimeData.Link);
490 }
491
492 CoreAcquireEventLock ();
493
494 if ((Type & EVT_NOTIFY_SIGNAL) != 0x00000000) {
495 //
496 // The Event's NotifyFunction must be queued whenever the event is signaled
497 //
498 InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
499 }
500
501 CoreReleaseEventLock ();
502
503 //
504 // Done
505 //
506 return EFI_SUCCESS;
507 }
508
509
510
511
512 /**
513 Signals the event. Queues the event to be notified if needed.
514
515 @param UserEvent The event to signal .
516
517 @retval EFI_INVALID_PARAMETER Parameters are not valid.
518 @retval EFI_SUCCESS The event was signaled.
519
520 **/
521 EFI_STATUS
522 EFIAPI
523 CoreSignalEvent (
524 IN EFI_EVENT UserEvent
525 )
526 {
527 IEVENT *Event;
528
529 Event = UserEvent;
530
531 if (Event == NULL) {
532 return EFI_INVALID_PARAMETER;
533 }
534
535 if (Event->Signature != EVENT_SIGNATURE) {
536 return EFI_INVALID_PARAMETER;
537 }
538
539 CoreAcquireEventLock ();
540
541 //
542 // If the event is not already signalled, do so
543 //
544
545 if (Event->SignalCount == 0x00000000) {
546 Event->SignalCount++;
547
548 //
549 // If signalling type is a notify function, queue it
550 //
551 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
552 if ((Event->ExFlag & EVT_EXFLAG_EVENT_GROUP) != 0) {
553 //
554 // The CreateEventEx() style requires all members of the Event Group
555 // to be signaled.
556 //
557 CoreReleaseEventLock ();
558 CoreNotifySignalList (&Event->EventGroup);
559 CoreAcquireEventLock ();
560 } else {
561 CoreNotifyEvent (Event);
562 }
563 }
564 }
565
566 CoreReleaseEventLock ();
567 return EFI_SUCCESS;
568 }
569
570
571
572 /**
573 Check the status of an event.
574
575 @param UserEvent The event to check
576
577 @retval EFI_SUCCESS The event is in the signaled state
578 @retval EFI_NOT_READY The event is not in the signaled state
579 @retval EFI_INVALID_PARAMETER Event is of type EVT_NOTIFY_SIGNAL
580
581 **/
582 EFI_STATUS
583 EFIAPI
584 CoreCheckEvent (
585 IN EFI_EVENT UserEvent
586 )
587 {
588 IEVENT *Event;
589 EFI_STATUS Status;
590
591 Event = UserEvent;
592
593 if (Event == NULL) {
594 return EFI_INVALID_PARAMETER;
595 }
596
597 if (Event->Signature != EVENT_SIGNATURE) {
598 return EFI_INVALID_PARAMETER;
599 }
600
601 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
602 return EFI_INVALID_PARAMETER;
603 }
604
605 Status = EFI_NOT_READY;
606
607 if ((Event->SignalCount == 0) && ((Event->Type & EVT_NOTIFY_WAIT) != 0)) {
608
609 //
610 // Queue the wait notify function
611 //
612 CoreAcquireEventLock ();
613 if (Event->SignalCount == 0) {
614 CoreNotifyEvent (Event);
615 }
616 CoreReleaseEventLock ();
617 }
618
619 //
620 // If the even looks signalled, get the lock and clear it
621 //
622
623 if (Event->SignalCount != 0) {
624 CoreAcquireEventLock ();
625
626 if (Event->SignalCount != 0) {
627 Event->SignalCount = 0;
628 Status = EFI_SUCCESS;
629 }
630
631 CoreReleaseEventLock ();
632 }
633
634 return Status;
635 }
636
637
638
639 /**
640 Stops execution until an event is signaled.
641
642 @param NumberOfEvents The number of events in the UserEvents array
643 @param UserEvents An array of EFI_EVENT
644 @param UserIndex Pointer to the index of the event which
645 satisfied the wait condition
646
647 @retval EFI_SUCCESS The event indicated by Index was signaled.
648 @retval EFI_INVALID_PARAMETER The event indicated by Index has a notification
649 function or Event was not a valid type
650 @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION
651
652 **/
653 EFI_STATUS
654 EFIAPI
655 CoreWaitForEvent (
656 IN UINTN NumberOfEvents,
657 IN EFI_EVENT *UserEvents,
658 OUT UINTN *UserIndex
659 )
660 {
661 EFI_STATUS Status;
662 UINTN Index;
663
664 //
665 // Can only WaitForEvent at TPL_APPLICATION
666 //
667 if (gEfiCurrentTpl != TPL_APPLICATION) {
668 return EFI_UNSUPPORTED;
669 }
670
671 if (NumberOfEvents == 0) {
672 return EFI_INVALID_PARAMETER;
673 }
674
675 if (UserEvents == NULL) {
676 return EFI_INVALID_PARAMETER;
677 }
678
679 for(;;) {
680
681 for(Index = 0; Index < NumberOfEvents; Index++) {
682
683 Status = CoreCheckEvent (UserEvents[Index]);
684
685 //
686 // provide index of event that caused problem
687 //
688 if (Status != EFI_NOT_READY) {
689 if (UserIndex != NULL) {
690 *UserIndex = Index;
691 }
692 return Status;
693 }
694 }
695
696 //
697 // Signal the Idle event
698 //
699 CoreSignalEvent (gIdleLoopEvent);
700 }
701 }
702
703
704 /**
705 Closes an event and frees the event structure.
706
707 @param UserEvent Event to close
708
709 @retval EFI_INVALID_PARAMETER Parameters are not valid.
710 @retval EFI_SUCCESS The event has been closed
711
712 **/
713 EFI_STATUS
714 EFIAPI
715 CoreCloseEvent (
716 IN EFI_EVENT UserEvent
717 )
718 {
719 EFI_STATUS Status;
720 IEVENT *Event;
721
722 Event = UserEvent;
723
724 if (Event == NULL) {
725 return EFI_INVALID_PARAMETER;
726 }
727
728 if (Event->Signature != EVENT_SIGNATURE) {
729 return EFI_INVALID_PARAMETER;
730 }
731
732 //
733 // If it's a timer event, make sure it's not pending
734 //
735 if ((Event->Type & EVT_TIMER) != 0) {
736 CoreSetTimer (Event, TimerCancel, 0);
737 }
738
739 CoreAcquireEventLock ();
740
741 //
742 // If the event is queued somewhere, remove it
743 //
744
745 if (Event->RuntimeData.Link.ForwardLink != NULL) {
746 RemoveEntryList (&Event->RuntimeData.Link);
747 }
748
749 if (Event->NotifyLink.ForwardLink != NULL) {
750 RemoveEntryList (&Event->NotifyLink);
751 }
752
753 if (Event->SignalLink.ForwardLink != NULL) {
754 RemoveEntryList (&Event->SignalLink);
755 }
756
757 CoreReleaseEventLock ();
758
759 //
760 // If the event is registered on a protocol notify, then remove it from the protocol database
761 //
762 if ((Event->ExFlag & EVT_EXFLAG_EVENT_PROTOCOL_NOTIFICATION) != 0) {
763 CoreUnregisterProtocolNotify (Event);
764 }
765
766 //
767 // To avoid the Event to be signalled wrongly after closed,
768 // clear the Signature of Event before free pool.
769 //
770 Event->Signature = 0;
771 Status = CoreFreePool (Event);
772 ASSERT_EFI_ERROR (Status);
773
774 return Status;
775 }
776