/*++ Copyright (c) 2000 Microsoft Corporation Module Name: object.c Abstract: This is the NT Watchdog driver implementation. Author: Michael Maciesowicz (mmacie) 02-May-2001 Environment: Kernel mode only. Notes: Revision History: --*/ #include "wd.h" #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, WdFlushRegistryKey) #pragma alloc_text (PAGE, WdInitializeObject) #endif // // Exports. // WATCHDOGAPI VOID WdCompleteEvent( IN PVOID pWatch, IN PKTHREAD pThread ) /*++ Routine Description: This function *MUST* be called from client handler for watchdog timeout event before exiting. It removes references from watchdog and thread objects. It also reenables watchdog event generation for deferred watchdog objects. Arguments: pWatch - Points to WATCHDOG_OBJECT. pThread - Points to KTHREAD object for spinning thread. Return Value: None. --*/ { // // Note: pThread is NULL for recovery events. // ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); ASSERT_WATCHDOG_OBJECT(pWatch); // // Resume event generation for deferred watchdog. // if (WdDeferredWatchdog == ((PWATCHDOG_OBJECT)pWatch)->ObjectType) { InterlockedExchange(&(((PDEFERRED_WATCHDOG)pWatch)->Trigger), 0); } // // Drop reference counts. // if (NULL != pThread) { ObDereferenceObject(pThread); } WdDereferenceObject(pWatch); return; } // WdCompleteEvent() WATCHDOGAPI VOID WdDereferenceObject( IN PVOID pWatch ) /*++ Routine Description: This function decreases reference count of watchdog object. If remaining count is zero we will remove object here, since it's been freed already. Arguments: pWatch - Points to WATCHDOG_OBJECT. Return Value: None. --*/ { PWATCHDOG_OBJECT pWatchdogObject; ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); ASSERT_WATCHDOG_OBJECT(pWatch); pWatchdogObject = (PWATCHDOG_OBJECT)pWatch; ASSERT(pWatchdogObject->ReferenceCount > 0); // // Drop reference count and remove the object if fully dereferenced. // if (InterlockedDecrement(&(pWatchdogObject->ReferenceCount)) == 0) { // // Object already freed - remove it now. // WdRemoveObject(pWatchdogObject); } return; } // WdDereferenceObject() WATCHDOGAPI PDEVICE_OBJECT WdGetDeviceObject( IN PVOID pWatch ) /*++ Routine Description: This function return pointer to device object associated with watchdog object. This function increases reference count on DEVICE_OBJECT so the caller must call ObDereferenceObject() once DEVICE_OBJECT pointer is not needed any more. Arguments: pWatch - Points to WATCHDOG_OBJECT. Return Value: Pointer to DEVICE_OBJECT. --*/ { PDEVICE_OBJECT pDeviceObject; ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); ASSERT_WATCHDOG_OBJECT(pWatch); pDeviceObject = ((PWATCHDOG_OBJECT)pWatch)->DeviceObject; ASSERT(NULL != pDeviceObject); ObReferenceObject(pDeviceObject); return pDeviceObject; } // WdGetDeviceObject() WATCHDOGAPI WD_EVENT_TYPE WdGetLastEvent( IN PVOID pWatch ) /*++ Routine Description: This function return last event associated with watchdog object. Arguments: pWatch - Points to WATCHDOG_OBJECT. Return Value: Last event type. --*/ { ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); ASSERT_WATCHDOG_OBJECT(pWatch); return ((PWATCHDOG_OBJECT)pWatch)->LastEvent; } // WdGetLastEvent() WATCHDOGAPI PDEVICE_OBJECT WdGetLowestDeviceObject( IN PVOID pWatch ) /*++ Routine Description: This function return pointer to the lowest (most likely PDO) DEVICE_OBJECT associated with watchdog object. This function increases reference count on returned DEVICE_OBJECT - the caller must call ObDereferenceObject() once DEVICE_OBJECT pointer is not needed any more. Arguments: pWatch - Points to WATCHDOG_OBJECT. Return Value: Pointer to DEVICE_OBJECT. --*/ { PDEVICE_OBJECT pDeviceObject; ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); ASSERT_WATCHDOG_OBJECT(pWatch); // // Note: No need to bump reference count here, it is always done when // watchdog object is created. // pDeviceObject = ((PWATCHDOG_OBJECT)pWatch)->DeviceObject; ASSERT(NULL != pDeviceObject); // // Now get the pointer to the lowest device object in the stack. // Note: This call automatically bumps a reference count on returned object. // pDeviceObject = IoGetDeviceAttachmentBaseRef(pDeviceObject); ASSERT(NULL != pDeviceObject); return pDeviceObject; } // WdGetLowestDeviceObject() WATCHDOGAPI VOID WdReferenceObject( IN PVOID pWatch ) /*++ Routine Description: This function increases reference count of watchdog object. Arguments: pWatch - Points to WATCHDOG_OBJECT. Return Value: None. --*/ { ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); ASSERT_WATCHDOG_OBJECT(pWatch); if (InterlockedIncrement(&(((PWATCHDOG_OBJECT)pWatch)->ReferenceCount)) == 1) { // // Somebody referenced removed object. // ASSERT(FALSE); } // // Check for overflow. // ASSERT(((PWATCHDOG_OBJECT)pWatch)->ReferenceCount > 0); return; } // WdReferenceObject() // // Non-exports. // NTSTATUS WdFlushRegistryKey( IN PVOID pWatch, IN PCWSTR pwszKeyName ) /*++ Routine Description: This function forces a registry key to be committed to disk. Arguments: pWatch - Points to WATCHDOG_OBJECT. pwszKeyName - Points to key name string. Return Value: Status code. --*/ { OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeKeyName; HANDLE keyHandle; NTSTATUS ntStatus; PAGED_CODE(); UNREFERENCED_PARAMETER(pWatch); ASSERT(NULL != pwszKeyName); RtlInitUnicodeString(&unicodeKeyName, pwszKeyName); InitializeObjectAttributes(&objectAttributes, &unicodeKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL); ntStatus = ZwOpenKey(&keyHandle, KEY_READ | KEY_WRITE, &objectAttributes); if (NT_SUCCESS(ntStatus)) { ntStatus = ZwFlushKey(keyHandle); ZwClose(keyHandle); } return ntStatus; } // WdFlushRegistryKey() VOID WdInitializeObject( IN PVOID pWatch, IN PDEVICE_OBJECT pDeviceObject, IN WD_OBJECT_TYPE objectType, IN WD_TIME_TYPE timeType, IN ULONG ulTag ) /*++ Routine Description: This function initializes watchdog object. Arguments: pWatch - Points to WATCHDOG_OBJECT. pDeviceObject - Points to DEVICE_OBJECT associated with watchdog. objectType - Type of watchdog object. timeType - Kernel, User, Both thread time to monitor. ulTag - A tag identifying owner. Return Value: None. --*/ { PWATCHDOG_OBJECT pWatchdogObject; PAGED_CODE(); ASSERT(NULL != pWatch); ASSERT(NULL != pDeviceObject); ASSERT((objectType == WdStandardWatchdog) || (objectType == WdDeferredWatchdog)); ASSERT((timeType >= WdKernelTime) && (timeType <= WdFullTime)); pWatchdogObject = (PWATCHDOG_OBJECT)pWatch; // // Set initial state of watchdog object. // pWatchdogObject->ObjectType = objectType; pWatchdogObject->ReferenceCount = 1; pWatchdogObject->OwnerTag = ulTag; pWatchdogObject->DeviceObject = pDeviceObject; pWatchdogObject->TimeType = timeType; pWatchdogObject->LastEvent = WdNoEvent; pWatchdogObject->LastQueuedThread = NULL; // // Bump reference count on device object. // ObReferenceObject(pDeviceObject); // // Initialize encapsulated KSPIN_LOCK object. // KeInitializeSpinLock(&(pWatchdogObject->SpinLock)); return; } // WdInitializeObject() VOID WdRemoveObject( IN PVOID pWatch ) /*++ Routine Description: This function unconditionally removes watchdog object. Arguments: pWatch - Points to WATCHDOG_OBJECT. Return Value: None. --*/ { ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); ASSERT_WATCHDOG_OBJECT(pWatch); ASSERT(0 == ((PWATCHDOG_OBJECT)pWatch)->ReferenceCount); // // Drop reference count on device object. // ObDereferenceObject(((PWATCHDOG_OBJECT)pWatch)->DeviceObject); // // We are freeing non-paged pool, it's OK to be at IRQL <= DISPATCH_LEVEL. // ExFreePool(pWatch); return; } // WdRemoveObject()