]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/Event/Event.c
09d51133618512a00d1ba313eed22195a3b066d1
[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) != 0) {
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 // Allocate and initialize a new event structure.
403 //
404 if ((Type & EVT_RUNTIME) != 0) {
405 IEvent = AllocateRuntimeZeroPool (sizeof (IEVENT));
406 } else {
407 IEvent = AllocateZeroPool (sizeof (IEVENT));
408 }
409 if (IEvent == NULL) {
410 return EFI_OUT_OF_RESOURCES;
411 }
412
413 IEvent->Signature = EVENT_SIGNATURE;
414 IEvent->Type = Type;
415
416 IEvent->NotifyTpl = NotifyTpl;
417 IEvent->NotifyFunction = NotifyFunction;
418 IEvent->NotifyContext = (VOID *)NotifyContext;
419 if (EventGroup != NULL) {
420 CopyGuid (&IEvent->EventGroup, EventGroup);
421 IEvent->ExFlag = TRUE;
422 }
423
424 *Event = IEvent;
425
426 if ((Type & EVT_RUNTIME) != 0) {
427 //
428 // Keep a list of all RT events so we can tell the RT AP.
429 //
430 IEvent->RuntimeData.Type = Type;
431 IEvent->RuntimeData.NotifyTpl = NotifyTpl;
432 IEvent->RuntimeData.NotifyFunction = NotifyFunction;
433 IEvent->RuntimeData.NotifyContext = (VOID *) NotifyContext;
434 IEvent->RuntimeData.Event = (EFI_EVENT *) IEvent;
435 InsertTailList (&gRuntime->EventHead, &IEvent->RuntimeData.Link);
436 }
437
438 CoreAcquireEventLock ();
439
440 if ((Type & EVT_NOTIFY_SIGNAL) != 0x00000000) {
441 //
442 // The Event's NotifyFunction must be queued whenever the event is signaled
443 //
444 InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
445 }
446
447 CoreReleaseEventLock ();
448
449 //
450 // Done
451 //
452 return EFI_SUCCESS;
453 }
454
455
456
457
458 /**
459 Signals the event. Queues the event to be notified if needed.
460
461 @param UserEvent The event to signal .
462
463 @retval EFI_INVALID_PARAMETER Parameters are not valid.
464 @retval EFI_SUCCESS The event was signaled.
465
466 **/
467 EFI_STATUS
468 EFIAPI
469 CoreSignalEvent (
470 IN EFI_EVENT UserEvent
471 )
472 {
473 IEVENT *Event;
474
475 Event = UserEvent;
476
477 if (Event == NULL) {
478 return EFI_INVALID_PARAMETER;
479 }
480
481 if (Event->Signature != EVENT_SIGNATURE) {
482 return EFI_INVALID_PARAMETER;
483 }
484
485 CoreAcquireEventLock ();
486
487 //
488 // If the event is not already signalled, do so
489 //
490
491 if (Event->SignalCount == 0x00000000) {
492 Event->SignalCount++;
493
494 //
495 // If signalling type is a notify function, queue it
496 //
497 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
498 if (Event->ExFlag) {
499 //
500 // The CreateEventEx() style requires all members of the Event Group
501 // to be signaled.
502 //
503 CoreReleaseEventLock ();
504 CoreNotifySignalList (&Event->EventGroup);
505 CoreAcquireEventLock ();
506 } else {
507 CoreNotifyEvent (Event);
508 }
509 }
510 }
511
512 CoreReleaseEventLock ();
513 return EFI_SUCCESS;
514 }
515
516
517
518 /**
519 Check the status of an event.
520
521 @param UserEvent The event to check
522
523 @retval EFI_SUCCESS The event is in the signaled state
524 @retval EFI_NOT_READY The event is not in the signaled state
525 @retval EFI_INVALID_PARAMETER Event is of type EVT_NOTIFY_SIGNAL
526
527 **/
528 EFI_STATUS
529 EFIAPI
530 CoreCheckEvent (
531 IN EFI_EVENT UserEvent
532 )
533 {
534 IEVENT *Event;
535 EFI_STATUS Status;
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 if ((Event->Type & EVT_NOTIFY_SIGNAL) != 0) {
548 return EFI_INVALID_PARAMETER;
549 }
550
551 Status = EFI_NOT_READY;
552
553 if ((Event->SignalCount == 0) && ((Event->Type & EVT_NOTIFY_WAIT) != 0)) {
554
555 //
556 // Queue the wait notify function
557 //
558 CoreAcquireEventLock ();
559 if (Event->SignalCount == 0) {
560 CoreNotifyEvent (Event);
561 }
562 CoreReleaseEventLock ();
563 }
564
565 //
566 // If the even looks signalled, get the lock and clear it
567 //
568
569 if (Event->SignalCount != 0) {
570 CoreAcquireEventLock ();
571
572 if (Event->SignalCount != 0) {
573 Event->SignalCount = 0;
574 Status = EFI_SUCCESS;
575 }
576
577 CoreReleaseEventLock ();
578 }
579
580 return Status;
581 }
582
583
584
585 /**
586 Stops execution until an event is signaled.
587
588 @param NumberOfEvents The number of events in the UserEvents array
589 @param UserEvents An array of EFI_EVENT
590 @param UserIndex Pointer to the index of the event which
591 satisfied the wait condition
592
593 @retval EFI_SUCCESS The event indicated by Index was signaled.
594 @retval EFI_INVALID_PARAMETER The event indicated by Index has a notification
595 function or Event was not a valid type
596 @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION
597
598 **/
599 EFI_STATUS
600 EFIAPI
601 CoreWaitForEvent (
602 IN UINTN NumberOfEvents,
603 IN EFI_EVENT *UserEvents,
604 OUT UINTN *UserIndex
605 )
606 {
607 EFI_STATUS Status;
608 UINTN Index;
609
610 //
611 // Can only WaitForEvent at TPL_APPLICATION
612 //
613 if (gEfiCurrentTpl != TPL_APPLICATION) {
614 return EFI_UNSUPPORTED;
615 }
616
617 for(;;) {
618
619 for(Index = 0; Index < NumberOfEvents; Index++) {
620
621 Status = CoreCheckEvent (UserEvents[Index]);
622
623 //
624 // provide index of event that caused problem
625 //
626 if (Status != EFI_NOT_READY) {
627 *UserIndex = Index;
628 return Status;
629 }
630 }
631
632 //
633 // This was the location of the Idle loop callback in EFI 1.x reference
634 // code. We don't have that concept in this base at this point.
635 //
636 }
637 }
638
639
640 /**
641 Closes an event and frees the event structure.
642
643 @param UserEvent Event to close
644
645 @retval EFI_INVALID_PARAMETER Parameters are not valid.
646 @retval EFI_SUCCESS The event has been closed
647
648 **/
649 EFI_STATUS
650 EFIAPI
651 CoreCloseEvent (
652 IN EFI_EVENT UserEvent
653 )
654 {
655 EFI_STATUS Status;
656 IEVENT *Event;
657
658 Event = UserEvent;
659
660 if (Event == NULL) {
661 return EFI_INVALID_PARAMETER;
662 }
663
664 if (Event->Signature != EVENT_SIGNATURE) {
665 return EFI_INVALID_PARAMETER;
666 }
667
668 //
669 // If it's a timer event, make sure it's not pending
670 //
671 if ((Event->Type & EVT_TIMER) != 0) {
672 CoreSetTimer (Event, TimerCancel, 0);
673 }
674
675 CoreAcquireEventLock ();
676
677 //
678 // If the event is queued somewhere, remove it
679 //
680
681 if (Event->RuntimeData.Link.ForwardLink != NULL) {
682 RemoveEntryList (&Event->RuntimeData.Link);
683 }
684
685 if (Event->NotifyLink.ForwardLink != NULL) {
686 RemoveEntryList (&Event->NotifyLink);
687 }
688
689 if (Event->SignalLink.ForwardLink != NULL) {
690 RemoveEntryList (&Event->SignalLink);
691 }
692
693 CoreReleaseEventLock ();
694
695 //
696 // If the event is registered on a protocol notify, then remove it from the protocol database
697 //
698 CoreUnregisterProtocolNotify (Event);
699
700 Status = CoreFreePool (Event);
701 ASSERT_EFI_ERROR (Status);
702
703 return Status;
704 }