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