]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/Event/Event.c
MdeModulePkg DXE Core: Add Idle event during BootServices WaitForEvent
[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 - 2011, 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 a general-purpose event structure.
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 a general-purpose event structure
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 EFI_STATUS Status;
344 IEVENT *IEvent;
345 INTN Index;
346
347
348 if (Event == NULL) {
349 return EFI_INVALID_PARAMETER;
350 }
351
352 //
353 // Check to make sure no reserved flags are set
354 //
355 Status = EFI_INVALID_PARAMETER;
356 for (Index = 0; Index < (sizeof (mEventTable) / sizeof (UINT32)); Index++) {
357 if (Type == mEventTable[Index]) {
358 Status = EFI_SUCCESS;
359 break;
360 }
361 }
362 if(EFI_ERROR (Status)) {
363 return EFI_INVALID_PARAMETER;
364 }
365
366 //
367 // Convert Event type for pre-defined Event groups
368 //
369 if (EventGroup != NULL) {
370 //
371 // For event group, type EVT_SIGNAL_EXIT_BOOT_SERVICES and EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
372 // are not valid
373 //
374 if ((Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) || (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)) {
375 return EFI_INVALID_PARAMETER;
376 }
377 if (CompareGuid (EventGroup, &gEfiEventExitBootServicesGuid)) {
378 Type = EVT_SIGNAL_EXIT_BOOT_SERVICES;
379 } else if (CompareGuid (EventGroup, &gEfiEventVirtualAddressChangeGuid)) {
380 Type = EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE;
381 }
382 } else {
383 //
384 // Convert EFI 1.10 Events to their UEFI 2.0 CreateEventEx mapping
385 //
386 if (Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) {
387 EventGroup = &gEfiEventExitBootServicesGuid;
388 } else if (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) {
389 EventGroup = &gEfiEventVirtualAddressChangeGuid;
390 }
391 }
392
393 //
394 // If it's a notify type of event, check its parameters
395 //
396 if ((Type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) != 0) {
397 //
398 // Check for an invalid NotifyFunction or NotifyTpl
399 //
400 if ((NotifyFunction == NULL) ||
401 (NotifyTpl <= TPL_APPLICATION) ||
402 (NotifyTpl >= TPL_HIGH_LEVEL)) {
403 return EFI_INVALID_PARAMETER;
404 }
405
406 } else {
407 //
408 // No notification needed, zero ignored values
409 //
410 NotifyTpl = 0;
411 NotifyFunction = NULL;
412 NotifyContext = NULL;
413 }
414
415 //
416 // Allocate and initialize a new event structure.
417 //
418 if ((Type & EVT_RUNTIME) != 0) {
419 IEvent = AllocateRuntimeZeroPool (sizeof (IEVENT));
420 } else {
421 IEvent = AllocateZeroPool (sizeof (IEVENT));
422 }
423 if (IEvent == NULL) {
424 return EFI_OUT_OF_RESOURCES;
425 }
426
427 IEvent->Signature = EVENT_SIGNATURE;
428 IEvent->Type = Type;
429
430 IEvent->NotifyTpl = NotifyTpl;
431 IEvent->NotifyFunction = NotifyFunction;
432 IEvent->NotifyContext = (VOID *)NotifyContext;
433 if (EventGroup != NULL) {
434 CopyGuid (&IEvent->EventGroup, EventGroup);
435 IEvent->ExFlag = TRUE;
436 }
437
438 *Event = IEvent;
439
440 if ((Type & EVT_RUNTIME) != 0) {
441 //
442 // Keep a list of all RT events so we can tell the RT AP.
443 //
444 IEvent->RuntimeData.Type = Type;
445 IEvent->RuntimeData.NotifyTpl = NotifyTpl;
446 IEvent->RuntimeData.NotifyFunction = NotifyFunction;
447 IEvent->RuntimeData.NotifyContext = (VOID *) NotifyContext;
448 IEvent->RuntimeData.Event = (EFI_EVENT *) IEvent;
449 InsertTailList (&gRuntime->EventHead, &IEvent->RuntimeData.Link);
450 }
451
452 CoreAcquireEventLock ();
453
454 if ((Type & EVT_NOTIFY_SIGNAL) != 0x00000000) {
455 //
456 // The Event's NotifyFunction must be queued whenever the event is signaled
457 //
458 InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
459 }
460
461 CoreReleaseEventLock ();
462
463 //
464 // Done
465 //
466 return EFI_SUCCESS;
467 }
468
469
470
471
472 /**
473 Signals the event. Queues the event to be notified if needed.
474
475 @param UserEvent The event to signal .
476
477 @retval EFI_INVALID_PARAMETER Parameters are not valid.
478 @retval EFI_SUCCESS The event was signaled.
479
480 **/
481 EFI_STATUS
482 EFIAPI
483 CoreSignalEvent (
484 IN EFI_EVENT UserEvent
485 )
486 {
487 IEVENT *Event;
488
489 Event = UserEvent;
490
491 if (Event == NULL) {
492 return EFI_INVALID_PARAMETER;
493 }
494
495 if (Event->Signature != EVENT_SIGNATURE) {
496 return EFI_INVALID_PARAMETER;
497 }
498
499 CoreAcquireEventLock ();
500
501 //
502 // If the event is not already signalled, do so
503 //
504
505 if (Event->SignalCount == 0x00000000) {
506 Event->SignalCount++;
507
508 //
509 // If signalling type is a notify function, queue it
510 //
511 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
512 if (Event->ExFlag) {
513 //
514 // The CreateEventEx() style requires all members of the Event Group
515 // to be signaled.
516 //
517 CoreReleaseEventLock ();
518 CoreNotifySignalList (&Event->EventGroup);
519 CoreAcquireEventLock ();
520 } else {
521 CoreNotifyEvent (Event);
522 }
523 }
524 }
525
526 CoreReleaseEventLock ();
527 return EFI_SUCCESS;
528 }
529
530
531
532 /**
533 Check the status of an event.
534
535 @param UserEvent The event to check
536
537 @retval EFI_SUCCESS The event is in the signaled state
538 @retval EFI_NOT_READY The event is not in the signaled state
539 @retval EFI_INVALID_PARAMETER Event is of type EVT_NOTIFY_SIGNAL
540
541 **/
542 EFI_STATUS
543 EFIAPI
544 CoreCheckEvent (
545 IN EFI_EVENT UserEvent
546 )
547 {
548 IEVENT *Event;
549 EFI_STATUS Status;
550
551 Event = UserEvent;
552
553 if (Event == NULL) {
554 return EFI_INVALID_PARAMETER;
555 }
556
557 if (Event->Signature != EVENT_SIGNATURE) {
558 return EFI_INVALID_PARAMETER;
559 }
560
561 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
562 return EFI_INVALID_PARAMETER;
563 }
564
565 Status = EFI_NOT_READY;
566
567 if ((Event->SignalCount == 0) && ((Event->Type & EVT_NOTIFY_WAIT) != 0)) {
568
569 //
570 // Queue the wait notify function
571 //
572 CoreAcquireEventLock ();
573 if (Event->SignalCount == 0) {
574 CoreNotifyEvent (Event);
575 }
576 CoreReleaseEventLock ();
577 }
578
579 //
580 // If the even looks signalled, get the lock and clear it
581 //
582
583 if (Event->SignalCount != 0) {
584 CoreAcquireEventLock ();
585
586 if (Event->SignalCount != 0) {
587 Event->SignalCount = 0;
588 Status = EFI_SUCCESS;
589 }
590
591 CoreReleaseEventLock ();
592 }
593
594 return Status;
595 }
596
597
598
599 /**
600 Stops execution until an event is signaled.
601
602 @param NumberOfEvents The number of events in the UserEvents array
603 @param UserEvents An array of EFI_EVENT
604 @param UserIndex Pointer to the index of the event which
605 satisfied the wait condition
606
607 @retval EFI_SUCCESS The event indicated by Index was signaled.
608 @retval EFI_INVALID_PARAMETER The event indicated by Index has a notification
609 function or Event was not a valid type
610 @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION
611
612 **/
613 EFI_STATUS
614 EFIAPI
615 CoreWaitForEvent (
616 IN UINTN NumberOfEvents,
617 IN EFI_EVENT *UserEvents,
618 OUT UINTN *UserIndex
619 )
620 {
621 EFI_STATUS Status;
622 UINTN Index;
623
624 //
625 // Can only WaitForEvent at TPL_APPLICATION
626 //
627 if (gEfiCurrentTpl != TPL_APPLICATION) {
628 return EFI_UNSUPPORTED;
629 }
630
631 for(;;) {
632
633 for(Index = 0; Index < NumberOfEvents; Index++) {
634
635 Status = CoreCheckEvent (UserEvents[Index]);
636
637 //
638 // provide index of event that caused problem
639 //
640 if (Status != EFI_NOT_READY) {
641 *UserIndex = Index;
642 return Status;
643 }
644 }
645
646 //
647 // Signal the Idle event
648 //
649 CoreSignalEvent (gIdleLoopEvent);
650 }
651 }
652
653
654 /**
655 Closes an event and frees the event structure.
656
657 @param UserEvent Event to close
658
659 @retval EFI_INVALID_PARAMETER Parameters are not valid.
660 @retval EFI_SUCCESS The event has been closed
661
662 **/
663 EFI_STATUS
664 EFIAPI
665 CoreCloseEvent (
666 IN EFI_EVENT UserEvent
667 )
668 {
669 EFI_STATUS Status;
670 IEVENT *Event;
671
672 Event = UserEvent;
673
674 if (Event == NULL) {
675 return EFI_INVALID_PARAMETER;
676 }
677
678 if (Event->Signature != EVENT_SIGNATURE) {
679 return EFI_INVALID_PARAMETER;
680 }
681
682 //
683 // If it's a timer event, make sure it's not pending
684 //
685 if ((Event->Type & EVT_TIMER) != 0) {
686 CoreSetTimer (Event, TimerCancel, 0);
687 }
688
689 CoreAcquireEventLock ();
690
691 //
692 // If the event is queued somewhere, remove it
693 //
694
695 if (Event->RuntimeData.Link.ForwardLink != NULL) {
696 RemoveEntryList (&Event->RuntimeData.Link);
697 }
698
699 if (Event->NotifyLink.ForwardLink != NULL) {
700 RemoveEntryList (&Event->NotifyLink);
701 }
702
703 if (Event->SignalLink.ForwardLink != NULL) {
704 RemoveEntryList (&Event->SignalLink);
705 }
706
707 CoreReleaseEventLock ();
708
709 //
710 // If the event is registered on a protocol notify, then remove it from the protocol database
711 //
712 CoreUnregisterProtocolNotify (Event);
713
714 Status = CoreFreePool (Event);
715 ASSERT_EFI_ERROR (Status);
716
717 return Status;
718 }
719