Windows2003-3790/drivers/watchdog/object.c

619 lines
12 KiB
C

/*++
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, WdpFlushRegistryKey)
#pragma alloc_text (PAGE, WdpInitializeObject)
#endif
//
// Exports.
//
WATCHDOGAPI
PVOID
WdAttachContext(
IN PVOID pWatch,
IN ULONG ulSize
)
/*++
Routine Description:
This routine allocates context buffer of given size from non-paged pool
and associates it with given watchdog object. In case the client code
doesn't detach the context before freeing watchdog object it will be
freed automatically when watchdog object is destroyed.
This routine helps to handle the case when watchdog object is freed
but the context must persist till object is actually destroyed and client
code cannot guarantee that.
Arguments:
pWatch - Points to WATCHDOG_OBJECT.
ulSize - Size in bytes of context to allocate and attach.
Return Value:
A pointer to created and attached context buffer.
--*/
{
PWATCHDOG_OBJECT pWatchdogObject;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT_WATCHDOG_OBJECT(pWatch);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdAttachContext);
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
//
// Check for double attach.
//
ASSERT(NULL == pWatchdogObject->Context);
//
// Allocate storage for attached context from non-paged pool.
//
pWatchdogObject->Context = ExAllocatePoolWithTag(NonPagedPool,
ulSize,
pWatchdogObject->OwnerTag);
return pWatchdogObject->Context;
} // WdAttachContext()
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);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdCompleteEvent);
//
// 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);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdDereferenceObject);
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.
//
WdpDestroyObject(pWatchdogObject);
}
return;
} // WdDereferenceObject()
WATCHDOGAPI
VOID
WdDetachContext(
IN PVOID pWatch
)
/*++
Routine Description:
This routine frees context attached to watchdog object.
NOTE: You don't have to call this routine before freeing watchdog
object - the context, if attached, will be destroyed automatically
when watchdog object is destroyed.
Arguments:
pWatch - Points to WATCHDOG_OBJECT.
Return Value:
None.
--*/
{
PWATCHDOG_OBJECT pWatchdogObject;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT_WATCHDOG_OBJECT(pWatch);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdDetachContext);
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
//
// Check for double detach.
//
ASSERT(NULL != pWatchdogObject->Context);
//
// Free storage for attached context and set reference to NULL.
//
// BUGBUG: This is not synchronized with DPCs!
//
ExFreePool(pWatchdogObject->Context);
pWatchdogObject->Context = NULL;
return;
} // WdDetachContext()
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);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdGetDeviceObject);
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);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdGetLastEvent);
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);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdGetLowestDeviceObject);
//
// 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);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdReferenceObject);
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.
//
VOID
WdpDestroyObject(
IN PVOID pWatch
)
/*++
Routine Description:
This function unconditionally frees watchdog object.
Arguments:
pWatch - Points to WATCHDOG_OBJECT.
Return Value:
None.
--*/
{
PWATCHDOG_OBJECT pWatchdogObject;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT_WATCHDOG_OBJECT(pWatch);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdpDestroyObject);
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
ASSERT(0 == pWatchdogObject->ReferenceCount);
//
// Drop reference count on device object.
//
ObDereferenceObject(pWatchdogObject->DeviceObject);
//
// We are freeing non-paged pool, it's OK to be at IRQL <= DISPATCH_LEVEL.
// Free attached context first, if any, then free watchdog object.
//
if (NULL != pWatchdogObject->Context)
{
ExFreePool(pWatchdogObject->Context);
}
ExFreePool(pWatch);
return;
} // WdpDestroyObject()
NTSTATUS
WdpFlushRegistryKey(
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);
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdpFlushRegistryKey);
RtlInitUnicodeString(&unicodeKeyName, pwszKeyName);
InitializeObjectAttributes(&objectAttributes,
&unicodeKeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
ntStatus = ZwOpenKey(&keyHandle,
KEY_READ | KEY_WRITE,
&objectAttributes);
if (NT_SUCCESS(ntStatus))
{
ntStatus = ZwFlushKey(keyHandle);
ZwClose(keyHandle);
}
return ntStatus;
} // WdpFlushRegistryKey()
VOID
WdpInitializeObject(
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));
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdpInitializeObject);
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;
pWatchdogObject->Context = NULL;
//
// Bump reference count on device object.
//
ObReferenceObject(pDeviceObject);
//
// Initialize encapsulated KSPIN_LOCK object.
//
KeInitializeSpinLock(&(pWatchdogObject->SpinLock));
return;
} // WdpInitializeObject()