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