]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/Event/Event.c
move header files in MdeModulePkg\Core\Dxe except DxeMain.h into their corresponding...
[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 - 2008, Intel Corporation. <BR>
5 All rights reserved. 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 queus
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 /**
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 return EFI_SUCCESS;
138 }
139
140
141
142 /**
143 Dispatches all pending events.
144
145 @param Priority The task priority level of event notifications
146 to dispatch
147
148 **/
149 VOID
150 CoreDispatchEventNotifies (
151 IN EFI_TPL Priority
152 )
153 {
154 IEVENT *Event;
155 LIST_ENTRY *Head;
156
157 CoreAcquireEventLock ();
158 ASSERT (gEventQueueLock.OwnerTpl == Priority);
159 Head = &gEventQueue[Priority];
160
161 //
162 // Dispatch all the pending notifications
163 //
164 while (!IsListEmpty (Head)) {
165
166 Event = CR (Head->ForwardLink, IEVENT, NotifyLink, EVENT_SIGNATURE);
167 RemoveEntryList (&Event->NotifyLink);
168
169 Event->NotifyLink.ForwardLink = NULL;
170
171 //
172 // Only clear the SIGNAL status if it is a SIGNAL type event.
173 // WAIT type events are only cleared in CheckEvent()
174 //
175 if (Event->Type & EVT_NOTIFY_SIGNAL) {
176 Event->SignalCount = 0;
177 }
178
179 CoreReleaseEventLock ();
180
181 //
182 // Notify this event
183 //
184 ASSERT (Event->NotifyFunction != NULL);
185 Event->NotifyFunction (Event, Event->NotifyContext);
186
187 //
188 // Check for next pending event
189 //
190 CoreAcquireEventLock ();
191 }
192
193 gEventPending &= ~(1 << Priority);
194 CoreReleaseEventLock ();
195 }
196
197
198
199 /**
200 Queues the event's notification function to fire.
201
202 @param Event The Event to notify
203
204 **/
205 VOID
206 CoreNotifyEvent (
207 IN IEVENT *Event
208 )
209 {
210
211 //
212 // Event database must be locked
213 //
214 ASSERT_LOCKED (&gEventQueueLock);
215
216 //
217 // If the event is queued somewhere, remove it
218 //
219
220 if (Event->NotifyLink.ForwardLink != NULL) {
221 RemoveEntryList (&Event->NotifyLink);
222 Event->NotifyLink.ForwardLink = NULL;
223 }
224
225 //
226 // Queue the event to the pending notification list
227 //
228
229 InsertTailList (&gEventQueue[Event->NotifyTpl], &Event->NotifyLink);
230 gEventPending |= (UINTN)(1 << Event->NotifyTpl);
231 }
232
233
234
235
236 /**
237 Signals all events in the EventGroup.
238
239 @param EventGroup The list to signal
240
241 **/
242 VOID
243 CoreNotifySignalList (
244 IN EFI_GUID *EventGroup
245 )
246 {
247 LIST_ENTRY *Link;
248 LIST_ENTRY *Head;
249 IEVENT *Event;
250
251 CoreAcquireEventLock ();
252
253 Head = &gEventSignalQueue;
254 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
255 Event = CR (Link, IEVENT, SignalLink, EVENT_SIGNATURE);
256 if (CompareGuid (&Event->EventGroup, EventGroup)) {
257 CoreNotifyEvent (Event);
258 }
259 }
260
261 CoreReleaseEventLock ();
262 }
263
264
265 /**
266 Creates a general-purpose event structure.
267
268 @param Type The type of event to create and its mode and
269 attributes
270 @param NotifyTpl The task priority level of event notifications
271 @param NotifyFunction Pointer to the events notification function
272 @param NotifyContext Pointer to the notification functions context;
273 corresponds to parameter "Context" in the
274 notification function
275 @param Event Pointer to the newly created event if the call
276 succeeds; undefined otherwise
277
278 @retval EFI_SUCCESS The event structure was created
279 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
280 @retval EFI_OUT_OF_RESOURCES The event could not be allocated
281
282 **/
283 EFI_STATUS
284 EFIAPI
285 CoreCreateEvent (
286 IN UINT32 Type,
287 IN EFI_TPL NotifyTpl,
288 IN EFI_EVENT_NOTIFY NotifyFunction, OPTIONAL
289 IN VOID *NotifyContext, OPTIONAL
290 OUT EFI_EVENT *Event
291 )
292 {
293 return CoreCreateEventEx (Type, NotifyTpl, NotifyFunction, NotifyContext, NULL, Event);
294 }
295
296
297
298 /**
299 Creates a general-purpose event structure
300
301 @param Type The type of event to create and its mode and
302 attributes
303 @param NotifyTpl The task priority level of event notifications
304 @param NotifyFunction Pointer to the events notification function
305 @param NotifyContext Pointer to the notification functions context;
306 corresponds to parameter "Context" in the
307 notification function
308 @param EventGroup GUID for EventGroup if NULL act the same as
309 gBS->CreateEvent().
310 @param Event Pointer to the newly created event if the call
311 succeeds; undefined otherwise
312
313 @retval EFI_SUCCESS The event structure was created
314 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value
315 @retval EFI_OUT_OF_RESOURCES The event could not be allocated
316
317 **/
318 EFI_STATUS
319 EFIAPI
320 CoreCreateEventEx (
321 IN UINT32 Type,
322 IN EFI_TPL NotifyTpl,
323 IN EFI_EVENT_NOTIFY NotifyFunction, OPTIONAL
324 IN CONST VOID *NotifyContext, OPTIONAL
325 IN CONST EFI_GUID *EventGroup, OPTIONAL
326 OUT EFI_EVENT *Event
327 )
328 {
329 EFI_STATUS Status;
330 IEVENT *IEvent;
331 INTN Index;
332
333
334 if (Event == NULL) {
335 return EFI_INVALID_PARAMETER;
336 }
337
338 //
339 // Check to make sure no reserved flags are set
340 //
341 Status = EFI_INVALID_PARAMETER;
342 for (Index = 0; Index < (sizeof (mEventTable) / sizeof (UINT32)); Index++) {
343 if (Type == mEventTable[Index]) {
344 Status = EFI_SUCCESS;
345 break;
346 }
347 }
348 if(EFI_ERROR (Status)) {
349 return EFI_INVALID_PARAMETER;
350 }
351
352 //
353 // Convert Event type for pre-defined Event groups
354 //
355 if (EventGroup != NULL) {
356 //
357 // For event group, type EVT_SIGNAL_EXIT_BOOT_SERVICES and EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
358 // are not valid
359 //
360 if ((Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) || (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)) {
361 return EFI_INVALID_PARAMETER;
362 }
363 if (CompareGuid (EventGroup, &gEfiEventExitBootServicesGuid)) {
364 Type = EVT_SIGNAL_EXIT_BOOT_SERVICES;
365 } else if (CompareGuid (EventGroup, &gEfiEventVirtualAddressChangeGuid)) {
366 Type = EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE;
367 }
368 } else {
369 //
370 // Convert EFI 1.10 Events to their UEFI 2.0 CreateEventEx mapping
371 //
372 if (Type == EVT_SIGNAL_EXIT_BOOT_SERVICES) {
373 EventGroup = &gEfiEventExitBootServicesGuid;
374 } else if (Type == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) {
375 EventGroup = &gEfiEventVirtualAddressChangeGuid;
376 }
377 }
378
379 //
380 // If it's a notify type of event, check its parameters
381 //
382 if ((Type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) != 0) {
383 //
384 // Check for an invalid NotifyFunction or NotifyTpl
385 //
386 if ((NotifyFunction == NULL) ||
387 (NotifyTpl <= TPL_APPLICATION) ||
388 (NotifyTpl >= TPL_HIGH_LEVEL)) {
389 return EFI_INVALID_PARAMETER;
390 }
391
392 } else {
393 //
394 // No notification needed, zero ignored values
395 //
396 NotifyTpl = 0;
397 NotifyFunction = NULL;
398 NotifyContext = NULL;
399 }
400
401 //
402 // Allcoate and initialize a new event structure.
403 //
404 Status = CoreAllocatePool (
405 ((Type & EVT_RUNTIME) != 0) ? EfiRuntimeServicesData: EfiBootServicesData,
406 sizeof (IEVENT),
407 (VOID **)&IEvent
408 );
409 if (EFI_ERROR (Status)) {
410 return EFI_OUT_OF_RESOURCES;
411 }
412
413 ZeroMem (IEvent, sizeof (IEVENT));
414
415 IEvent->Signature = EVENT_SIGNATURE;
416 IEvent->Type = Type;
417
418 IEvent->NotifyTpl = NotifyTpl;
419 IEvent->NotifyFunction = NotifyFunction;
420 IEvent->NotifyContext = (VOID *)NotifyContext;
421 if (EventGroup != NULL) {
422 CopyGuid (&IEvent->EventGroup, EventGroup);
423 IEvent->ExFlag = TRUE;
424 }
425
426 *Event = IEvent;
427
428 if ((Type & EVT_RUNTIME) != 0) {
429 //
430 // Keep a list of all RT events so we can tell the RT AP.
431 //
432 IEvent->RuntimeData.Type = Type;
433 IEvent->RuntimeData.NotifyTpl = NotifyTpl;
434 IEvent->RuntimeData.NotifyFunction = NotifyFunction;
435 IEvent->RuntimeData.NotifyContext = (VOID *) NotifyContext;
436 IEvent->RuntimeData.Event = (EFI_EVENT *) IEvent;
437 InsertTailList (&gRuntime->EventHead, &IEvent->RuntimeData.Link);
438 }
439
440 CoreAcquireEventLock ();
441
442 if ((Type & EVT_NOTIFY_SIGNAL) != 0x00000000) {
443 //
444 // The Event's NotifyFunction must be queued whenever the event is signaled
445 //
446 InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
447 }
448
449 CoreReleaseEventLock ();
450
451 //
452 // Done
453 //
454 return EFI_SUCCESS;
455 }
456
457
458
459
460 /**
461 Signals the event. Queues the event to be notified if needed.
462
463 @param UserEvent The event to signal .
464
465 @retval EFI_INVALID_PARAMETER Parameters are not valid.
466 @retval EFI_SUCCESS The event was signaled.
467
468 **/
469 EFI_STATUS
470 EFIAPI
471 CoreSignalEvent (
472 IN EFI_EVENT UserEvent
473 )
474 {
475 IEVENT *Event;
476
477 Event = UserEvent;
478
479 if (Event == NULL) {
480 return EFI_INVALID_PARAMETER;
481 }
482
483 if (Event->Signature != EVENT_SIGNATURE) {
484 return EFI_INVALID_PARAMETER;
485 }
486
487 CoreAcquireEventLock ();
488
489 //
490 // If the event is not already signalled, do so
491 //
492
493 if (Event->SignalCount == 0x00000000) {
494 Event->SignalCount++;
495
496 //
497 // If signalling type is a notify function, queue it
498 //
499 if (Event->Type & EVT_NOTIFY_SIGNAL) {
500 if (Event->ExFlag) {
501 //
502 // The CreateEventEx() style requires all members of the Event Group
503 // to be signaled.
504 //
505 CoreReleaseEventLock ();
506 CoreNotifySignalList (&Event->EventGroup);
507 CoreAcquireEventLock ();
508 } else {
509 CoreNotifyEvent (Event);
510 }
511 }
512 }
513
514 CoreReleaseEventLock ();
515 return EFI_SUCCESS;
516 }
517
518
519
520 /**
521 Check the status of an event.
522
523 @param UserEvent The event to check
524
525 @retval EFI_SUCCESS The event is in the signaled state
526 @retval EFI_NOT_READY The event is not in the signaled state
527 @retval EFI_INVALID_PARAMETER Event is of type EVT_NOTIFY_SIGNAL
528
529 **/
530 EFI_STATUS
531 EFIAPI
532 CoreCheckEvent (
533 IN EFI_EVENT UserEvent
534 )
535 {
536 IEVENT *Event;
537 EFI_STATUS Status;
538
539 Event = UserEvent;
540
541 if (Event == NULL) {
542 return EFI_INVALID_PARAMETER;
543 }
544
545 if (Event->Signature != EVENT_SIGNATURE) {
546 return EFI_INVALID_PARAMETER;
547 }
548
549 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
550 return EFI_INVALID_PARAMETER;
551 }
552
553 Status = EFI_NOT_READY;
554
555 if (!Event->SignalCount && (Event->Type & EVT_NOTIFY_WAIT)) {
556
557 //
558 // Queue the wait notify function
559 //
560 CoreAcquireEventLock ();
561 if (!Event->SignalCount) {
562 CoreNotifyEvent (Event);
563 }
564 CoreReleaseEventLock ();
565 }
566
567 //
568 // If the even looks signalled, get the lock and clear it
569 //
570
571 if (Event->SignalCount) {
572 CoreAcquireEventLock ();
573
574 if (Event->SignalCount) {
575 Event->SignalCount = 0;
576 Status = EFI_SUCCESS;
577 }
578
579 CoreReleaseEventLock ();
580 }
581
582 return Status;
583 }
584
585
586
587 /**
588 Stops execution until an event is signaled.
589
590 @param NumberOfEvents The number of events in the UserEvents array
591 @param UserEvents An array of EFI_EVENT
592 @param UserIndex Pointer to the index of the event which
593 satisfied the wait condition
594
595 @retval EFI_SUCCESS The event indicated by Index was signaled.
596 @retval EFI_INVALID_PARAMETER The event indicated by Index has a notification
597 function or Event was not a valid type
598 @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION
599
600 **/
601 EFI_STATUS
602 EFIAPI
603 CoreWaitForEvent (
604 IN UINTN NumberOfEvents,
605 IN EFI_EVENT *UserEvents,
606 OUT UINTN *UserIndex
607 )
608 {
609 EFI_STATUS Status;
610 UINTN Index;
611
612 //
613 // Can only WaitForEvent at TPL_APPLICATION
614 //
615 if (gEfiCurrentTpl != TPL_APPLICATION) {
616 return EFI_UNSUPPORTED;
617 }
618
619 for(;;) {
620
621 for(Index = 0; Index < NumberOfEvents; Index++) {
622
623 Status = CoreCheckEvent (UserEvents[Index]);
624
625 //
626 // provide index of event that caused problem
627 //
628 if (Status != EFI_NOT_READY) {
629 *UserIndex = Index;
630 return Status;
631 }
632 }
633
634 //
635 // This was the location of the Idle loop callback in EFI 1.x reference
636 // code. We don't have that concept in this base at this point.
637 //
638 }
639 }
640
641
642 /**
643 Closes an event and frees the event structure.
644
645 @param UserEvent Event to close
646
647 @retval EFI_INVALID_PARAMETER Parameters are not valid.
648 @retval EFI_SUCCESS The event has been closed
649
650 **/
651 EFI_STATUS
652 EFIAPI
653 CoreCloseEvent (
654 IN EFI_EVENT UserEvent
655 )
656 {
657 EFI_STATUS Status;
658 IEVENT *Event;
659
660 Event = UserEvent;
661
662 if (Event == NULL) {
663 return EFI_INVALID_PARAMETER;
664 }
665
666 if (Event->Signature != EVENT_SIGNATURE) {
667 return EFI_INVALID_PARAMETER;
668 }
669
670 //
671 // If it's a timer event, make sure it's not pending
672 //
673 if ((Event->Type & EVT_TIMER) != 0) {
674 CoreSetTimer (Event, TimerCancel, 0);
675 }
676
677 CoreAcquireEventLock ();
678
679 //
680 // If the event is queued somewhere, remove it
681 //
682
683 if (Event->RuntimeData.Link.ForwardLink != NULL) {
684 RemoveEntryList (&Event->RuntimeData.Link);
685 }
686
687 if (Event->NotifyLink.ForwardLink != NULL) {
688 RemoveEntryList (&Event->NotifyLink);
689 }
690
691 if (Event->SignalLink.ForwardLink != NULL) {
692 RemoveEntryList (&Event->SignalLink);
693 }
694
695 CoreReleaseEventLock ();
696
697 //
698 // If the event is registered on a protocol notify, then remove it from the protocol database
699 //
700 CoreUnregisterProtocolNotify (Event);
701
702 Status = CoreFreePool (Event);
703 ASSERT_EFI_ERROR (Status);
704
705 return Status;
706 }