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