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