reactos/ntoskrnl/ex/callback.c

689 lines
20 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ex/callback.c
* PURPOSE: Executive callbacks
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* TYPES *********************************************************************/
/* Mapping for Callback Object */
GENERIC_MAPPING ExpCallbackMapping =
{
STANDARD_RIGHTS_READ,
STANDARD_RIGHTS_WRITE | CALLBACK_MODIFY_STATE,
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
CALLBACK_ALL_ACCESS
};
/* Kernel Default Callbacks */
PCALLBACK_OBJECT SetSystemTimeCallback;
PCALLBACK_OBJECT SetSystemStateCallback;
PCALLBACK_OBJECT PowerStateCallback;
SYSTEM_CALLBACKS ExpInitializeCallback[] =
{
{&SetSystemTimeCallback, L"\\Callback\\SetSystemTime"},
{&SetSystemStateCallback, L"\\Callback\\SetSystemState"},
{&PowerStateCallback, L"\\Callback\\PowerState"},
{NULL, NULL}
};
POBJECT_TYPE ExCallbackObjectType;
KEVENT ExpCallbackEvent;
EX_PUSH_LOCK ExpCallBackFlush;
/* PRIVATE FUNCTIONS *********************************************************/
VOID
NTAPI
ExInitializeCallBack(IN OUT PEX_CALLBACK Callback)
{
/* Initialize the fast reference */
ExInitializeFastReference(&Callback->RoutineBlock, NULL);
}
PEX_CALLBACK_ROUTINE_BLOCK
NTAPI
ExAllocateCallBack(IN PEX_CALLBACK_FUNCTION Function,
IN PVOID Context)
{
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
/* Allocate a callback */
CallbackBlock = ExAllocatePoolWithTag(PagedPool,
sizeof(EX_CALLBACK_ROUTINE_BLOCK),
TAG_CALLBACK_ROUTINE_BLOCK);
if (CallbackBlock)
{
/* Initialize it */
CallbackBlock->Function = Function;
CallbackBlock->Context = Context;
ExInitializeRundownProtection(&CallbackBlock->RundownProtect);
}
/* Return it */
return CallbackBlock;
}
VOID
NTAPI
ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Just free it from memory */
ExFreePoolWithTag(CallbackBlock, TAG_CALLBACK_ROUTINE_BLOCK);
}
VOID
NTAPI
ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Wait on the rundown */
ExWaitForRundownProtectionRelease(&CallbackBlock->RundownProtect);
}
PEX_CALLBACK_FUNCTION
NTAPI
ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Return the function */
return CallbackBlock->Function;
}
PVOID
NTAPI
ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Return the context */
return CallbackBlock->Context;
}
VOID
NTAPI
ExDereferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack,
IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
{
/* Release a fast reference */
if (!ExReleaseFastReference(&CallBack->RoutineBlock, CallbackBlock))
{
/* Take slow path */
ExReleaseRundownProtection(&CallbackBlock->RundownProtect);
}
}
PEX_CALLBACK_ROUTINE_BLOCK
NTAPI
ExReferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack)
{
EX_FAST_REF OldValue;
ULONG_PTR Count;
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
/* Acquire a reference */
OldValue = ExAcquireFastReference(&CallBack->RoutineBlock);
Count = ExGetCountFastReference(OldValue);
/* Fail if there isn't any object */
if (!ExGetObjectFastReference(OldValue)) return NULL;
/* Check if we don't have a reference */
if (!Count)
{
/* FIXME: Race */
DPRINT1("Unhandled callback race condition\n");
ASSERT(FALSE);
return NULL;
}
/* Get the callback block */
CallbackBlock = ExGetObjectFastReference(OldValue);
/* Check if this is the last reference */
if (Count == 1)
{
/* Acquire rundown protection */
if (ExfAcquireRundownProtectionEx(&CallbackBlock->RundownProtect,
MAX_FAST_REFS))
{
/* Insert references */
if (!ExInsertFastReference(&CallBack->RoutineBlock, CallbackBlock))
{
/* Backdown the rundown acquire */
ExfReleaseRundownProtectionEx(&CallbackBlock->RundownProtect,
MAX_FAST_REFS);
}
}
}
/* Return the callback block */
return CallbackBlock;
}
BOOLEAN
NTAPI
ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack,
IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock,
IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock)
{
EX_FAST_REF OldValue;
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
ULONG Count;
/* Check that we have a new block */
if (NewBlock)
{
/* Acquire rundown */
if (!ExfAcquireRundownProtectionEx(&NewBlock->RundownProtect,
MAX_FAST_REFS + 1))
{
/* This should never happen */
ASSERTMSG("Callback block is already undergoing rundown", FALSE);
return FALSE;
}
}
/* Do the swap */
OldValue = ExCompareSwapFastReference(&CallBack->RoutineBlock,
NewBlock,
OldBlock);
/* Get the routine block */
CallbackBlock = ExGetObjectFastReference(OldValue);
Count = ExGetCountFastReference(OldValue);
/* Make sure the swap worked */
if (CallbackBlock == OldBlock)
{
/* Make sure we replaced a valid pointer */
if (CallbackBlock)
{
/* Acquire the flush lock and immediately release it */
KeEnterCriticalRegion();
ExWaitOnPushLock(&ExpCallBackFlush);
/* Release rundown protection */
KeLeaveCriticalRegion();
ExfReleaseRundownProtectionEx(&CallbackBlock->RundownProtect,
Count + 1);
}
/* Compare worked */
return TRUE;
}
else
{
/* It failed, check if we had a block */
if (NewBlock)
{
/* We did, remove the references that we had added */
ExfReleaseRundownProtectionEx(&NewBlock->RundownProtect,
MAX_FAST_REFS + 1);
}
/* Return failure */
return FALSE;
}
}
VOID
NTAPI
ExpDeleteCallback(IN PVOID Object)
{
/* Sanity check */
ASSERT(IsListEmpty(&((PCALLBACK_OBJECT)Object)->RegisteredCallbacks));
}
/*++
* @name ExpInitializeCallbacks
*
* Creates the Callback Object as a valid Object Type in the Kernel.
* Internal function, subject to further review
*
* @return TRUE if the Callback Object Type was successfully created.
*
* @remarks None
*
*--*/
BOOLEAN
INIT_FUNCTION
NTAPI
ExpInitializeCallbacks(VOID)
{
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
UNICODE_STRING DirName = RTL_CONSTANT_STRING(L"\\Callback");
UNICODE_STRING CallbackName;
UNICODE_STRING Name;
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
HANDLE DirectoryHandle;
ULONG i;
/* Setup lightweight callback lock */
ExpCallBackFlush.Value = 0;
/* Initialize the Callback Object type */
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
RtlInitUnicodeString(&Name, L"Callback");
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
ObjectTypeInitializer.GenericMapping = ExpCallbackMapping;
ObjectTypeInitializer.PoolType = NonPagedPool;
ObjectTypeInitializer.DeleteProcedure = ExpDeleteCallback;
ObjectTypeInitializer.ValidAccessMask = CALLBACK_ALL_ACCESS;
Status = ObCreateObjectType(&Name,
&ObjectTypeInitializer,
NULL,
&ExCallbackObjectType);
if (!NT_SUCCESS(Status)) return FALSE;
/* Initialize the Object */
InitializeObjectAttributes(&ObjectAttributes,
&DirName,
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
NULL,
SePublicDefaultSd);
/* Create the Object Directory */
Status = NtCreateDirectoryObject(&DirectoryHandle,
DIRECTORY_ALL_ACCESS,
&ObjectAttributes);
if (!NT_SUCCESS(Status)) return FALSE;
/* Close Handle... */
NtClose(DirectoryHandle);
/* Initialize Event used when unregistering */
KeInitializeEvent(&ExpCallbackEvent, NotificationEvent, 0);
/* Default NT Kernel Callbacks. */
for (i = 0; ExpInitializeCallback[i].CallbackObject; i++)
{
/* Create the name from the structure */
RtlInitUnicodeString(&CallbackName, ExpInitializeCallback[i].Name);
/* Initialize the Object Attributes Structure */
InitializeObjectAttributes(&ObjectAttributes,
&CallbackName,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
NULL,
NULL);
/* Create the Callback Object */
Status = ExCreateCallback(ExpInitializeCallback[i].CallbackObject,
&ObjectAttributes,
TRUE,
TRUE);
if (!NT_SUCCESS(Status)) return FALSE;
}
/* Everything successful */
return TRUE;
}
/* PUBLIC FUNCTIONS **********************************************************/
/*++
* @name ExCreateCallback
* @implemented
*
* Opens or creates a Callback Object. Creates only if Create is true.
* Allows multiple Callback Functions to be registered only if
* AllowMultipleCallbacks is true.
* See: http://www.osronline.com/ddkx/kmarch/k102_967m.htm
* http://www.osronline.com/article.cfm?id=24
*
* @param CallbackObject
* Pointer that will receive the Callback Object.
*
* @param CallbackName
* Name of Callback
*
* @param Create
* Determines if the object will be created if it doesn't exit
*
* @param AllowMultipleCallbacks
* Determines if more then one registered callback function
* can be attached to this Callback Object.
*
* @return STATUS_SUCESS if not failed.
*
* @remarks Must be called at IRQL = PASSIVE_LEVEL
*
*--*/
NTSTATUS
NTAPI
ExCreateCallback(OUT PCALLBACK_OBJECT *CallbackObject,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN BOOLEAN Create,
IN BOOLEAN AllowMultipleCallbacks)
{
PCALLBACK_OBJECT Callback = NULL;
NTSTATUS Status;
HANDLE Handle = NULL;
PAGED_CODE();
/* Open a handle to the callback if it exists */
if (ObjectAttributes->ObjectName)
{
/* Open the handle */
Status = ObOpenObjectByName(ObjectAttributes,
ExCallbackObjectType,
KernelMode,
NULL,
0,
NULL,
&Handle);
}
else
{
/* Otherwise, fail */
Status = STATUS_UNSUCCESSFUL;
}
/* We weren't able to open it...should we create it? */
if (!(NT_SUCCESS(Status)) && (Create))
{
/* Create the object */
Status = ObCreateObject(KernelMode,
ExCallbackObjectType,
ObjectAttributes,
KernelMode,
NULL,
sizeof(CALLBACK_OBJECT),
0,
0,
(PVOID *)&Callback);
if (NT_SUCCESS(Status))
{
/* Set it up */
Callback->Signature = 'llaC';
KeInitializeSpinLock(&Callback->Lock);
InitializeListHead(&Callback->RegisteredCallbacks);
Callback->AllowMultipleCallbacks = AllowMultipleCallbacks;
/* Insert the object into the object namespace */
Status = ObInsertObject(Callback,
NULL,
FILE_READ_DATA,
0,
NULL,
&Handle);
}
}
/* Check if we have success until here */
if (NT_SUCCESS(Status))
{
/* Get a pointer to the new object from the handle we just got */
Status = ObReferenceObjectByHandle(Handle,
0,
ExCallbackObjectType,
KernelMode,
(PVOID *)&Callback,
NULL);
/* Close the Handle, since we now have the pointer */
ZwClose(Handle);
}
/* Everything went fine, so return a pointer to the Object */
if (NT_SUCCESS(Status))
{
*CallbackObject = Callback;
}
return Status;
}
/*++
* @name ExNotifyCallback
* @implemented
*
* Calls a function pointer (a registered callback)
* See: http://www.osronline.com/ddkx/kmarch/k102_2f5e.htm
* http://vmsone.com/~decuslib/vmssig/vmslt99b/nt/wdm-callback.txt
*
* @param CallbackObject
* Which callback to call
*
* @param Argument1
* Pointer/data to send to callback function
*
* @param Argument2
* Pointer/data to send to callback function
*
* @return None
*
* @remarks None
*
*--*/
VOID
NTAPI
ExNotifyCallback(IN PCALLBACK_OBJECT CallbackObject,
IN PVOID Argument1,
IN PVOID Argument2)
{
PLIST_ENTRY RegisteredCallbacks;
PCALLBACK_REGISTRATION CallbackRegistration;
KIRQL OldIrql;
/* Check if we don't have an object or registrations */
if (!(CallbackObject) ||
(IsListEmpty(&CallbackObject->RegisteredCallbacks)))
{
/* Don't notify */
return;
}
/* Acquire the Lock */
KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
/* Enumerate through all the registered functions */
for (RegisteredCallbacks = CallbackObject->RegisteredCallbacks.Flink;
RegisteredCallbacks != &CallbackObject->RegisteredCallbacks;
RegisteredCallbacks = RegisteredCallbacks->Flink)
{
/* Get a pointer to a Callback Registration from the List Entries */
CallbackRegistration = CONTAINING_RECORD(RegisteredCallbacks,
CALLBACK_REGISTRATION,
Link);
/* Don't bother doing notification if it's pending to be deleted */
if (!CallbackRegistration->UnregisterWaiting)
{
/* Mark the Callback in use, so it won't get deleted */
CallbackRegistration->Busy += 1;
/* Release the Spinlock before making the call */
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
/* Call the Registered Function */
CallbackRegistration->CallbackFunction(CallbackRegistration->
CallbackContext,
Argument1,
Argument2);
/* Get SpinLock back */
KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
/* We are not in use anymore */
CallbackRegistration->Busy -= 1;
/* Check if removal is pending and we're not active */
if ((CallbackRegistration->UnregisterWaiting) &&
!(CallbackRegistration->Busy))
{
/* Signal the callback event */
KeSetEvent(&ExpCallbackEvent, 0, FALSE);
}
}
}
/* Release the Callback Object */
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
}
/*++
* @name ExRegisterCallback
* @implemented
*
* Allows a function to associate a callback pointer (Function) to
* a created Callback object
* See: DDK, OSR, links in ExNotifyCallback
*
* @param CallbackObject
* The Object Created with ExCreateCallBack
*
* @param CallBackFunction
* Pointer to the function to be called back
*
* @param CallBackContext
* Block of memory that can contain user-data which will be
* passed on to the callback
*
* @return A handle to a Callback Registration Structure (MSDN Documentation)
*
* @remarks None
*
*--*/
PVOID
NTAPI
ExRegisterCallback(IN PCALLBACK_OBJECT CallbackObject,
IN PCALLBACK_FUNCTION CallbackFunction,
IN PVOID CallbackContext)
{
PCALLBACK_REGISTRATION CallbackRegistration = NULL;
KIRQL OldIrql;
/* Sanity checks */
ASSERT(CallbackFunction);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Create reference to Callback Object */
ObReferenceObject(CallbackObject);
/* Allocate memory for the structure */
CallbackRegistration = ExAllocatePoolWithTag(NonPagedPool,
sizeof(CALLBACK_REGISTRATION),
TAG_CALLBACK_REGISTRATION);
if (!CallbackRegistration)
{
/* Dereference and fail */
ObDereferenceObject (CallbackObject);
return NULL;
}
/* Create Callback Registration */
CallbackRegistration->CallbackObject = CallbackObject;
CallbackRegistration->CallbackFunction = CallbackFunction;
CallbackRegistration->CallbackContext = CallbackContext;
CallbackRegistration->Busy = 0;
CallbackRegistration->UnregisterWaiting = FALSE;
/* Acquire SpinLock */
KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
/* Check if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
if ((CallbackObject->AllowMultipleCallbacks) ||
(IsListEmpty(&CallbackObject->RegisteredCallbacks)))
{
/* Register the callback */
InsertTailList(&CallbackObject->RegisteredCallbacks,
&CallbackRegistration->Link);
/* Release SpinLock */
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
}
else
{
/* Release SpinLock */
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
/* Free the registration */
ExFreePoolWithTag(CallbackRegistration, TAG_CALLBACK_REGISTRATION);
CallbackRegistration = NULL;
/* Dereference the object */
ObDereferenceObject(CallbackObject);
}
/* Return handle to Registration Object */
return (PVOID)CallbackRegistration;
}
/*++
* @name ExUnregisterCallback
* @implemented
*
* Deregisters a CallBack
* See: DDK, OSR, links in ExNotifyCallback
*
* @param CallbackRegistration
* Callback Registration Handle
*
* @return None
*
* @remarks None
*
*--*/
VOID
NTAPI
ExUnregisterCallback(IN PVOID CallbackRegistrationHandle)
{
PCALLBACK_REGISTRATION CallbackRegistration;
PCALLBACK_OBJECT CallbackObject;
KIRQL OldIrql;
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Convert Handle to valid Structure Pointer */
CallbackRegistration = (PCALLBACK_REGISTRATION)CallbackRegistrationHandle;
/* Get the Callback Object */
CallbackObject = CallbackRegistration->CallbackObject;
/* Lock the Object */
KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
/* We can't Delete the Callback if it's in use */
while (CallbackRegistration->Busy)
{
/* Let everyone else know we're unregistering */
CallbackRegistration->UnregisterWaiting = TRUE;
/* We are going to wait for the event, so the Lock isn't necessary */
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
/* Make sure the event is cleared */
KeClearEvent(&ExpCallbackEvent);
/* Wait for the Event */
KeWaitForSingleObject(&ExpCallbackEvent,
Executive,
KernelMode,
FALSE,
NULL);
/* We need the Lock again */
KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
}
/* Remove the Callback */
RemoveEntryList(&CallbackRegistration->Link);
/* It's now safe to release the lock */
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
/* Delete this registration */
ExFreePoolWithTag(CallbackRegistration, TAG_CALLBACK_REGISTRATION);
/* Remove the reference */
ObDereferenceObject(CallbackObject);
}
/* EOF */