1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ex/callback.c 5 * PURPOSE: Executive callbacks 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* TYPES *********************************************************************/ 16 17 /* Mapping for Callback Object */ 18 GENERIC_MAPPING ExpCallbackMapping = 19 { 20 STANDARD_RIGHTS_READ, 21 STANDARD_RIGHTS_WRITE | CALLBACK_MODIFY_STATE, 22 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, 23 CALLBACK_ALL_ACCESS 24 }; 25 26 /* Kernel Default Callbacks */ 27 PCALLBACK_OBJECT SetSystemTimeCallback; 28 PCALLBACK_OBJECT SetSystemStateCallback; 29 PCALLBACK_OBJECT PowerStateCallback; 30 SYSTEM_CALLBACKS ExpInitializeCallback[] = 31 { 32 {&SetSystemTimeCallback, L"\\Callback\\SetSystemTime"}, 33 {&SetSystemStateCallback, L"\\Callback\\SetSystemState"}, 34 {&PowerStateCallback, L"\\Callback\\PowerState"}, 35 {NULL, NULL} 36 }; 37 38 POBJECT_TYPE ExCallbackObjectType; 39 KEVENT ExpCallbackEvent; 40 EX_PUSH_LOCK ExpCallBackFlush; 41 42 /* PRIVATE FUNCTIONS *********************************************************/ 43 44 VOID 45 NTAPI 46 ExInitializeCallBack(IN OUT PEX_CALLBACK Callback) 47 { 48 /* Initialize the fast reference */ 49 ExInitializeFastReference(&Callback->RoutineBlock, NULL); 50 } 51 52 PEX_CALLBACK_ROUTINE_BLOCK 53 NTAPI 54 ExAllocateCallBack(IN PEX_CALLBACK_FUNCTION Function, 55 IN PVOID Context) 56 { 57 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock; 58 59 /* Allocate a callback */ 60 CallbackBlock = ExAllocatePoolWithTag(PagedPool, 61 sizeof(EX_CALLBACK_ROUTINE_BLOCK), 62 TAG_CALLBACK_ROUTINE_BLOCK); 63 if (CallbackBlock) 64 { 65 /* Initialize it */ 66 CallbackBlock->Function = Function; 67 CallbackBlock->Context = Context; 68 ExInitializeRundownProtection(&CallbackBlock->RundownProtect); 69 } 70 71 /* Return it */ 72 return CallbackBlock; 73 } 74 75 VOID 76 NTAPI 77 ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock) 78 { 79 /* Just free it from memory */ 80 ExFreePoolWithTag(CallbackBlock, TAG_CALLBACK_ROUTINE_BLOCK); 81 } 82 83 VOID 84 NTAPI 85 ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock) 86 { 87 /* Wait on the rundown */ 88 ExWaitForRundownProtectionRelease(&CallbackBlock->RundownProtect); 89 } 90 91 PEX_CALLBACK_FUNCTION 92 NTAPI 93 ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock) 94 { 95 /* Return the function */ 96 return CallbackBlock->Function; 97 } 98 99 PVOID 100 NTAPI 101 ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock) 102 { 103 /* Return the context */ 104 return CallbackBlock->Context; 105 } 106 107 VOID 108 NTAPI 109 ExDereferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack, 110 IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock) 111 { 112 /* Release a fast reference */ 113 if (!ExReleaseFastReference(&CallBack->RoutineBlock, CallbackBlock)) 114 { 115 /* Take slow path */ 116 ExReleaseRundownProtection(&CallbackBlock->RundownProtect); 117 } 118 } 119 120 PEX_CALLBACK_ROUTINE_BLOCK 121 NTAPI 122 ExReferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack) 123 { 124 EX_FAST_REF OldValue; 125 ULONG_PTR Count; 126 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock; 127 128 /* Acquire a reference */ 129 OldValue = ExAcquireFastReference(&CallBack->RoutineBlock); 130 Count = ExGetCountFastReference(OldValue); 131 132 /* Fail if there isn't any object */ 133 if (!ExGetObjectFastReference(OldValue)) return NULL; 134 135 /* Check if we don't have a reference */ 136 if (!Count) 137 { 138 /* FIXME: Race */ 139 DPRINT1("Unhandled callback race condition\n"); 140 ASSERT(FALSE); 141 return NULL; 142 } 143 144 /* Get the callback block */ 145 CallbackBlock = ExGetObjectFastReference(OldValue); 146 147 /* Check if this is the last reference */ 148 if (Count == 1) 149 { 150 /* Acquire rundown protection */ 151 if (ExfAcquireRundownProtectionEx(&CallbackBlock->RundownProtect, 152 MAX_FAST_REFS)) 153 { 154 /* Insert references */ 155 if (!ExInsertFastReference(&CallBack->RoutineBlock, CallbackBlock)) 156 { 157 /* Backdown the rundown acquire */ 158 ExfReleaseRundownProtectionEx(&CallbackBlock->RundownProtect, 159 MAX_FAST_REFS); 160 } 161 } 162 } 163 164 /* Return the callback block */ 165 return CallbackBlock; 166 } 167 168 BOOLEAN 169 NTAPI 170 ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack, 171 IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock, 172 IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock) 173 { 174 EX_FAST_REF OldValue; 175 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock; 176 ULONG Count; 177 178 /* Check that we have a new block */ 179 if (NewBlock) 180 { 181 /* Acquire rundown */ 182 if (!ExfAcquireRundownProtectionEx(&NewBlock->RundownProtect, 183 MAX_FAST_REFS + 1)) 184 { 185 /* This should never happen */ 186 ASSERTMSG("Callback block is already undergoing rundown\n", FALSE); 187 return FALSE; 188 } 189 } 190 191 /* Do the swap */ 192 OldValue = ExCompareSwapFastReference(&CallBack->RoutineBlock, 193 NewBlock, 194 OldBlock); 195 196 /* Get the routine block */ 197 CallbackBlock = ExGetObjectFastReference(OldValue); 198 Count = ExGetCountFastReference(OldValue); 199 200 /* Make sure the swap worked */ 201 if (CallbackBlock == OldBlock) 202 { 203 /* Make sure we replaced a valid pointer */ 204 if (CallbackBlock) 205 { 206 /* Acquire the flush lock and immediately release it */ 207 KeEnterCriticalRegion(); 208 ExWaitOnPushLock(&ExpCallBackFlush); 209 210 /* Release rundown protection */ 211 KeLeaveCriticalRegion(); 212 ExfReleaseRundownProtectionEx(&CallbackBlock->RundownProtect, 213 Count + 1); 214 } 215 216 /* Compare worked */ 217 return TRUE; 218 } 219 else 220 { 221 /* It failed, check if we had a block */ 222 if (NewBlock) 223 { 224 /* We did, remove the references that we had added */ 225 ExfReleaseRundownProtectionEx(&NewBlock->RundownProtect, 226 MAX_FAST_REFS + 1); 227 } 228 229 /* Return failure */ 230 return FALSE; 231 } 232 } 233 234 VOID 235 NTAPI 236 ExpDeleteCallback(IN PVOID Object) 237 { 238 /* Sanity check */ 239 ASSERT(IsListEmpty(&((PCALLBACK_OBJECT)Object)->RegisteredCallbacks)); 240 } 241 242 /*++ 243 * @name ExpInitializeCallbacks 244 * 245 * Creates the Callback Object as a valid Object Type in the Kernel. 246 * Internal function, subject to further review 247 * 248 * @return TRUE if the Callback Object Type was successfully created. 249 * 250 * @remarks None 251 * 252 *--*/ 253 CODE_SEG("INIT") 254 BOOLEAN 255 NTAPI 256 ExpInitializeCallbacks(VOID) 257 { 258 OBJECT_ATTRIBUTES ObjectAttributes; 259 NTSTATUS Status; 260 UNICODE_STRING DirName = RTL_CONSTANT_STRING(L"\\Callback"); 261 UNICODE_STRING CallbackName; 262 UNICODE_STRING Name; 263 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 264 HANDLE DirectoryHandle; 265 ULONG i; 266 267 /* Setup lightweight callback lock */ 268 ExpCallBackFlush.Value = 0; 269 270 /* Initialize the Callback Object type */ 271 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); 272 RtlInitUnicodeString(&Name, L"Callback"); 273 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); 274 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 275 ObjectTypeInitializer.GenericMapping = ExpCallbackMapping; 276 ObjectTypeInitializer.PoolType = NonPagedPool; 277 ObjectTypeInitializer.DeleteProcedure = ExpDeleteCallback; 278 ObjectTypeInitializer.ValidAccessMask = CALLBACK_ALL_ACCESS; 279 Status = ObCreateObjectType(&Name, 280 &ObjectTypeInitializer, 281 NULL, 282 &ExCallbackObjectType); 283 if (!NT_SUCCESS(Status)) return FALSE; 284 285 /* Initialize the Object */ 286 InitializeObjectAttributes(&ObjectAttributes, 287 &DirName, 288 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, 289 NULL, 290 SePublicDefaultSd); 291 292 /* Create the Object Directory */ 293 Status = NtCreateDirectoryObject(&DirectoryHandle, 294 DIRECTORY_ALL_ACCESS, 295 &ObjectAttributes); 296 if (!NT_SUCCESS(Status)) return FALSE; 297 298 /* Close Handle... */ 299 NtClose(DirectoryHandle); 300 301 /* Initialize Event used when unregistering */ 302 KeInitializeEvent(&ExpCallbackEvent, NotificationEvent, 0); 303 304 /* Default NT Kernel Callbacks. */ 305 for (i = 0; ExpInitializeCallback[i].CallbackObject; i++) 306 { 307 /* Create the name from the structure */ 308 RtlInitUnicodeString(&CallbackName, ExpInitializeCallback[i].Name); 309 310 /* Initialize the Object Attributes Structure */ 311 InitializeObjectAttributes(&ObjectAttributes, 312 &CallbackName, 313 OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, 314 NULL, 315 NULL); 316 317 /* Create the Callback Object */ 318 Status = ExCreateCallback(ExpInitializeCallback[i].CallbackObject, 319 &ObjectAttributes, 320 TRUE, 321 TRUE); 322 if (!NT_SUCCESS(Status)) return FALSE; 323 } 324 325 /* Everything successful */ 326 return TRUE; 327 } 328 329 /* PUBLIC FUNCTIONS **********************************************************/ 330 331 /*++ 332 * @name ExCreateCallback 333 * @implemented 334 * 335 * Opens or creates a Callback Object. Creates only if Create is true. 336 * Allows multiple Callback Functions to be registered only if 337 * AllowMultipleCallbacks is true. 338 * See: http://www.osronline.com/ddkx/kmarch/k102_967m.htm 339 * http://www.osronline.com/article.cfm?id=24 340 * 341 * @param CallbackObject 342 * Pointer that will receive the Callback Object. 343 * 344 * @param CallbackName 345 * Name of Callback 346 * 347 * @param Create 348 * Determines if the object will be created if it doesn't exit 349 * 350 * @param AllowMultipleCallbacks 351 * Determines if more then one registered callback function 352 * can be attached to this Callback Object. 353 * 354 * @return STATUS_SUCESS if not failed. 355 * 356 * @remarks Must be called at IRQL = PASSIVE_LEVEL 357 * 358 *--*/ 359 NTSTATUS 360 NTAPI 361 ExCreateCallback(OUT PCALLBACK_OBJECT *CallbackObject, 362 IN POBJECT_ATTRIBUTES ObjectAttributes, 363 IN BOOLEAN Create, 364 IN BOOLEAN AllowMultipleCallbacks) 365 { 366 PCALLBACK_OBJECT Callback = NULL; 367 NTSTATUS Status; 368 HANDLE Handle = NULL; 369 PAGED_CODE(); 370 371 /* Open a handle to the callback if it exists */ 372 if (ObjectAttributes->ObjectName) 373 { 374 /* Open the handle */ 375 Status = ObOpenObjectByName(ObjectAttributes, 376 ExCallbackObjectType, 377 KernelMode, 378 NULL, 379 0, 380 NULL, 381 &Handle); 382 } 383 else 384 { 385 /* Otherwise, fail */ 386 Status = STATUS_UNSUCCESSFUL; 387 } 388 389 /* We weren't able to open it...should we create it? */ 390 if (!(NT_SUCCESS(Status)) && (Create)) 391 { 392 /* Create the object */ 393 Status = ObCreateObject(KernelMode, 394 ExCallbackObjectType, 395 ObjectAttributes, 396 KernelMode, 397 NULL, 398 sizeof(CALLBACK_OBJECT), 399 0, 400 0, 401 (PVOID *)&Callback); 402 if (NT_SUCCESS(Status)) 403 { 404 /* Set it up */ 405 Callback->Signature = 'llaC'; 406 KeInitializeSpinLock(&Callback->Lock); 407 InitializeListHead(&Callback->RegisteredCallbacks); 408 Callback->AllowMultipleCallbacks = AllowMultipleCallbacks; 409 410 /* Insert the object into the object namespace */ 411 Status = ObInsertObject(Callback, 412 NULL, 413 FILE_READ_DATA, 414 0, 415 NULL, 416 &Handle); 417 } 418 } 419 420 /* Check if we have success until here */ 421 if (NT_SUCCESS(Status)) 422 { 423 /* Get a pointer to the new object from the handle we just got */ 424 Status = ObReferenceObjectByHandle(Handle, 425 0, 426 ExCallbackObjectType, 427 KernelMode, 428 (PVOID *)&Callback, 429 NULL); 430 431 /* Close the Handle, since we now have the pointer */ 432 ZwClose(Handle); 433 } 434 435 /* Everything went fine, so return a pointer to the Object */ 436 if (NT_SUCCESS(Status)) 437 { 438 *CallbackObject = Callback; 439 } 440 return Status; 441 } 442 443 /*++ 444 * @name ExNotifyCallback 445 * @implemented 446 * 447 * Calls a function pointer (a registered callback) 448 * See: http://www.osronline.com/ddkx/kmarch/k102_2f5e.htm 449 * http://vmsone.com/~decuslib/vmssig/vmslt99b/nt/wdm-callback.txt 450 * 451 * @param CallbackObject 452 * Which callback to call 453 * 454 * @param Argument1 455 * Pointer/data to send to callback function 456 * 457 * @param Argument2 458 * Pointer/data to send to callback function 459 * 460 * @return None 461 * 462 * @remarks None 463 * 464 *--*/ 465 VOID 466 NTAPI 467 ExNotifyCallback(IN PCALLBACK_OBJECT CallbackObject, 468 IN PVOID Argument1, 469 IN PVOID Argument2) 470 { 471 PLIST_ENTRY RegisteredCallbacks; 472 PCALLBACK_REGISTRATION CallbackRegistration; 473 KIRQL OldIrql; 474 475 /* Check if we don't have an object or registrations */ 476 if (!(CallbackObject) || 477 (IsListEmpty(&CallbackObject->RegisteredCallbacks))) 478 { 479 /* Don't notify */ 480 return; 481 } 482 483 /* Acquire the Lock */ 484 KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql); 485 486 /* Enumerate through all the registered functions */ 487 for (RegisteredCallbacks = CallbackObject->RegisteredCallbacks.Flink; 488 RegisteredCallbacks != &CallbackObject->RegisteredCallbacks; 489 RegisteredCallbacks = RegisteredCallbacks->Flink) 490 { 491 /* Get a pointer to a Callback Registration from the List Entries */ 492 CallbackRegistration = CONTAINING_RECORD(RegisteredCallbacks, 493 CALLBACK_REGISTRATION, 494 Link); 495 496 /* Don't bother doing notification if it's pending to be deleted */ 497 if (!CallbackRegistration->UnregisterWaiting) 498 { 499 /* Mark the Callback in use, so it won't get deleted */ 500 CallbackRegistration->Busy += 1; 501 502 /* Release the Spinlock before making the call */ 503 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql); 504 505 /* Call the Registered Function */ 506 CallbackRegistration->CallbackFunction(CallbackRegistration-> 507 CallbackContext, 508 Argument1, 509 Argument2); 510 511 /* Get SpinLock back */ 512 KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql); 513 514 /* We are not in use anymore */ 515 CallbackRegistration->Busy -= 1; 516 517 /* Check if removal is pending and we're not active */ 518 if ((CallbackRegistration->UnregisterWaiting) && 519 !(CallbackRegistration->Busy)) 520 { 521 /* Signal the callback event */ 522 KeSetEvent(&ExpCallbackEvent, 0, FALSE); 523 } 524 } 525 } 526 527 /* Release the Callback Object */ 528 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql); 529 } 530 531 /*++ 532 * @name ExRegisterCallback 533 * @implemented 534 * 535 * Allows a function to associate a callback pointer (Function) to 536 * a created Callback object 537 * See: DDK, OSR, links in ExNotifyCallback 538 * 539 * @param CallbackObject 540 * The Object Created with ExCreateCallBack 541 * 542 * @param CallBackFunction 543 * Pointer to the function to be called back 544 * 545 * @param CallBackContext 546 * Block of memory that can contain user-data which will be 547 * passed on to the callback 548 * 549 * @return A handle to a Callback Registration Structure (MSDN Documentation) 550 * 551 * @remarks None 552 * 553 *--*/ 554 PVOID 555 NTAPI 556 ExRegisterCallback(IN PCALLBACK_OBJECT CallbackObject, 557 IN PCALLBACK_FUNCTION CallbackFunction, 558 IN PVOID CallbackContext) 559 { 560 PCALLBACK_REGISTRATION CallbackRegistration = NULL; 561 KIRQL OldIrql; 562 563 /* Sanity checks */ 564 ASSERT(CallbackFunction); 565 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); 566 567 /* Create reference to Callback Object */ 568 ObReferenceObject(CallbackObject); 569 570 /* Allocate memory for the structure */ 571 CallbackRegistration = ExAllocatePoolWithTag(NonPagedPool, 572 sizeof(CALLBACK_REGISTRATION), 573 TAG_CALLBACK_REGISTRATION); 574 if (!CallbackRegistration) 575 { 576 /* Dereference and fail */ 577 ObDereferenceObject (CallbackObject); 578 return NULL; 579 } 580 581 /* Create Callback Registration */ 582 CallbackRegistration->CallbackObject = CallbackObject; 583 CallbackRegistration->CallbackFunction = CallbackFunction; 584 CallbackRegistration->CallbackContext = CallbackContext; 585 CallbackRegistration->Busy = 0; 586 CallbackRegistration->UnregisterWaiting = FALSE; 587 588 /* Acquire SpinLock */ 589 KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql); 590 591 /* Check if 1) No Callbacks registered or 2) Multiple Callbacks allowed */ 592 if ((CallbackObject->AllowMultipleCallbacks) || 593 (IsListEmpty(&CallbackObject->RegisteredCallbacks))) 594 { 595 /* Register the callback */ 596 InsertTailList(&CallbackObject->RegisteredCallbacks, 597 &CallbackRegistration->Link); 598 599 /* Release SpinLock */ 600 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql); 601 } 602 else 603 { 604 /* Release SpinLock */ 605 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql); 606 607 /* Free the registration */ 608 ExFreePoolWithTag(CallbackRegistration, TAG_CALLBACK_REGISTRATION); 609 CallbackRegistration = NULL; 610 611 /* Dereference the object */ 612 ObDereferenceObject(CallbackObject); 613 } 614 615 /* Return handle to Registration Object */ 616 return (PVOID)CallbackRegistration; 617 } 618 619 /*++ 620 * @name ExUnregisterCallback 621 * @implemented 622 * 623 * Deregisters a CallBack 624 * See: DDK, OSR, links in ExNotifyCallback 625 * 626 * @param CallbackRegistration 627 * Callback Registration Handle 628 * 629 * @return None 630 * 631 * @remarks None 632 * 633 *--*/ 634 VOID 635 NTAPI 636 ExUnregisterCallback(IN PVOID CallbackRegistrationHandle) 637 { 638 PCALLBACK_REGISTRATION CallbackRegistration; 639 PCALLBACK_OBJECT CallbackObject; 640 KIRQL OldIrql; 641 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); 642 643 /* Convert Handle to valid Structure Pointer */ 644 CallbackRegistration = (PCALLBACK_REGISTRATION)CallbackRegistrationHandle; 645 646 /* Get the Callback Object */ 647 CallbackObject = CallbackRegistration->CallbackObject; 648 649 /* Lock the Object */ 650 KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql); 651 652 /* We can't Delete the Callback if it's in use */ 653 while (CallbackRegistration->Busy) 654 { 655 /* Let everyone else know we're unregistering */ 656 CallbackRegistration->UnregisterWaiting = TRUE; 657 658 /* We are going to wait for the event, so the Lock isn't necessary */ 659 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql); 660 661 /* Make sure the event is cleared */ 662 KeClearEvent(&ExpCallbackEvent); 663 664 /* Wait for the Event */ 665 KeWaitForSingleObject(&ExpCallbackEvent, 666 Executive, 667 KernelMode, 668 FALSE, 669 NULL); 670 671 /* We need the Lock again */ 672 KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql); 673 } 674 675 /* Remove the Callback */ 676 RemoveEntryList(&CallbackRegistration->Link); 677 678 /* It's now safe to release the lock */ 679 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql); 680 681 /* Delete this registration */ 682 ExFreePoolWithTag(CallbackRegistration, TAG_CALLBACK_REGISTRATION); 683 684 /* Remove the reference */ 685 ObDereferenceObject(CallbackObject); 686 } 687 688 /* EOF */ 689