summaryrefslogtreecommitdiffstats
path: root/private/ntos/ex/timer.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/ex/timer.c
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/ex/timer.c')
-rw-r--r--private/ntos/ex/timer.c1148
1 files changed, 1148 insertions, 0 deletions
diff --git a/private/ntos/ex/timer.c b/private/ntos/ex/timer.c
new file mode 100644
index 000000000..af193e72d
--- /dev/null
+++ b/private/ntos/ex/timer.c
@@ -0,0 +1,1148 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ timer.c
+
+Abstract:
+
+ This module implements the executive timer object. Functions are provided
+ to create, open, cancel, set, and query timer objects.
+
+Author:
+
+ David N. Cutler (davec) 12-May-1989
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "exp.h"
+
+//
+// Executive timer object structure definition.
+//
+
+typedef struct _ETIMER {
+ KTIMER KeTimer;
+ KAPC TimerApc;
+ KDPC TimerDpc;
+ LIST_ENTRY ActiveTimerListEntry;
+ KSPIN_LOCK Lock;
+ LONG Period;
+ BOOLEAN ApcAssociated;
+} ETIMER, *PETIMER;
+
+extern KPCR KiTpcr;
+
+//
+// Address of timer object type descriptor.
+//
+
+POBJECT_TYPE ExTimerObjectType;
+
+//
+// Structure that describes the mapping of generic access rights to object
+// specific access rights for timer objects.
+//
+
+GENERIC_MAPPING ExpTimerMapping = {
+ STANDARD_RIGHTS_READ |
+ TIMER_QUERY_STATE,
+ STANDARD_RIGHTS_WRITE |
+ TIMER_MODIFY_STATE,
+ STANDARD_RIGHTS_EXECUTE |
+ SYNCHRONIZE,
+ TIMER_ALL_ACCESS
+};
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, ExpTimerInitialization)
+#pragma alloc_text(PAGE, NtCreateTimer)
+#pragma alloc_text(PAGE, NtOpenTimer)
+#pragma alloc_text(PAGE, NtQueryTimer)
+#endif
+
+VOID
+ExpTimerApcRoutine (
+ IN PKAPC Apc,
+ IN PKNORMAL_ROUTINE *NormalRoutine,
+ IN PVOID *NormalContext,
+ IN PVOID *SystemArgument1,
+ IN PVOID *SystemArgument2
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the special APC routine that is called to remove
+ a timer from the current thread's active timer list.
+
+Arguments:
+
+ Apc - Supplies a pointer to the APC object used to invoke this routine.
+
+ NormalRoutine - Supplies a pointer to a pointer to the normal routine
+ function that was specified when the APC was initialized.
+
+ NormalContext - Supplies a pointer to a pointer to an arbitrary data
+ structure that was specified when the APC was initialized.
+
+ SystemArgument1, SystemArgument2 - Supplies a set of two pointers to
+ two arguments that contain untyped data.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ BOOLEAN Dereference;
+ PETHREAD ExThread;
+ PETIMER ExTimer;
+ KIRQL OldIrql1;
+ KIRQL OldIrql2;
+
+ //
+ // Get address of executive timer object and the current thread object.
+ //
+
+ ExThread = PsGetCurrentThread();
+ ExTimer = CONTAINING_RECORD(Apc, ETIMER, TimerApc);
+
+ //
+ // If the timer is still in the current thread's active timer list, then
+ // remove it if it is not a periodic timer and set APC associated FALSE.
+ // It is possible for the timer not to be in the current thread's active
+ // timer list since the APC could have been delivered, and then another
+ // thread could have set the timer again with another APC. This would
+ // have caused the timer to be removed from the current thread's active
+ // timer list.
+ //
+ // N. B. The spin locks for the timer and the active timer list must be
+ // acquired in the order: 1) timer lock, 2) thread list lock.
+ //
+
+ Dereference = FALSE;
+ ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1);
+ ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql2);
+ if ((ExTimer->ApcAssociated) && (&ExThread->Tcb == ExTimer->TimerApc.Thread)) {
+ if (ExTimer->Period == 0) {
+ RemoveEntryList(&ExTimer->ActiveTimerListEntry);
+ ExTimer->ApcAssociated = FALSE;
+ Dereference = TRUE;
+ }
+
+ } else {
+ *NormalRoutine = (PKNORMAL_ROUTINE)NULL;
+ }
+
+ ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql2);
+ ExReleaseSpinLock(&ExTimer->Lock, OldIrql1);
+ if (Dereference) {
+ ObDereferenceObject((PVOID)ExTimer);
+ }
+
+ return;
+}
+
+VOID
+ExpTimerDpcRoutine (
+ IN PKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the DPC routine that is called when a timer expires that
+ has an associated APC routine. Its function is to insert the associated
+ APC into the target thread's APC queue.
+
+Arguments:
+
+ Dpc - Supplies a pointer to a control object of type DPC.
+
+ DeferredContext - Supplies a pointer to the executive timer that contains
+ the DPC that caused this routine to be executed.
+
+ SystemArgument1, SystemArgument2 - Supplies values that are not used by
+ this routine.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PETIMER ExTimer;
+ PKTIMER KeTimer;
+ KIRQL OldIrql;
+
+ //
+ // Get address of executive and kernel timer objects.
+ //
+
+ ExTimer = (PETIMER)DeferredContext;
+ KeTimer = &ExTimer->KeTimer;
+
+ //
+ // If there is still an APC associated with the timer, then insert the APC
+ // in target thread's APC queue. It is possible that the timer does not
+ // have an associated APC. This can happen when the timer is set to expire
+ // by a thread running on another processor just after the DPC has been
+ // removed from the DPC queue, but before it has acquired the timer related
+ // spin lock.
+ //
+
+ ExAcquireSpinLock(&ExTimer->Lock, &OldIrql);
+ if (ExTimer->ApcAssociated) {
+ KeInsertQueueApc(&ExTimer->TimerApc,
+ SystemArgument1,
+ SystemArgument2,
+ TIMER_APC_INCREMENT);
+ }
+
+ ExReleaseSpinLock(&ExTimer->Lock, OldIrql);
+ return;
+}
+
+static VOID
+ExpDeleteTimer (
+ IN PVOID Object
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the delete routine for timer objects. Its function is
+ to cancel the timer and free the spin lock associated with a timer.
+
+Arguments:
+
+ Object - Supplies a pointer to an executive timer object.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Cancel the timer and free the spin lock associated with the timer.
+ //
+
+ KeCancelTimer(&((PETIMER)Object)->KeTimer);
+ return;
+}
+
+BOOLEAN
+ExpTimerInitialization (
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates the timer object type descriptor at system
+ initialization and stores the address of the object type descriptor
+ in local static storage.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ A value of TRUE is returned if the timer object type descriptor is
+ successfully initialized. Otherwise a value of FALSE is returned.
+
+--*/
+
+{
+
+ OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
+ NTSTATUS Status;
+ UNICODE_STRING TypeName;
+
+ //
+ // Initialize string descriptor.
+ //
+
+ RtlInitUnicodeString(&TypeName, L"Timer");
+
+ //
+ // Create timer object type descriptor.
+ //
+
+ RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
+ ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
+ ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
+ ObjectTypeInitializer.GenericMapping = ExpTimerMapping;
+ ObjectTypeInitializer.PoolType = NonPagedPool;
+ ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETIMER);
+ ObjectTypeInitializer.ValidAccessMask = TIMER_ALL_ACCESS;
+ ObjectTypeInitializer.DeleteProcedure = ExpDeleteTimer;
+ Status = ObCreateObjectType(&TypeName,
+ &ObjectTypeInitializer,
+ (PSECURITY_DESCRIPTOR)NULL,
+ &ExTimerObjectType);
+
+ //
+ // If the time object type descriptor was successfully created, then
+ // return a value of TRUE. Otherwise return a value of FALSE.
+ //
+
+ return (BOOLEAN)(NT_SUCCESS(Status));
+}
+
+VOID
+ExTimerRundown (
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called when a thread is about to be terminated to
+ process the active timer list. It is assumed that APC's have been
+ disabled for the subject thread, thus this code cannot be interrupted
+ to execute an APC for the current thread.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ BOOLEAN Dereference;
+ PETHREAD ExThread;
+ PETIMER ExTimer;
+ PLIST_ENTRY NextEntry;
+ KIRQL OldIrql1;
+ KIRQL OldIrql2;
+
+ //
+ // Process each entry in the active timer list.
+ //
+
+ ExThread = PsGetCurrentThread();
+ ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql1);
+ NextEntry = ExThread->ActiveTimerListHead.Flink;
+ while (NextEntry != &ExThread->ActiveTimerListHead) {
+ ExTimer = CONTAINING_RECORD(NextEntry, ETIMER, ActiveTimerListEntry);
+
+ //
+ // Increment the reference count on the object so that it cannot be
+ // deleted, and then drop the active timer list lock.
+ //
+ // N. B. The object reference cannot fail and will acquire no mutexes.
+ //
+
+ ObReferenceObject(ExTimer);
+ ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql1);
+
+ //
+ // Acquire the timer spin lock and reacquire the active time list spin
+ // lock. If the timer is still in the current thread's active timer
+ // list, then cancel the timer, remove the timer's DPC from the DPC
+ // queue, remove the timer's APC from the APC queue, remove the timer
+ // from the thread's active timer list, and set the associate APC
+ // flag FALSE.
+ //
+ // N. B. The spin locks for the timer and the active timer list must be
+ // acquired in the order: 1) timer lock, 2) thread list lock.
+ //
+
+ ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1);
+ ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql2);
+ if ((ExTimer->ApcAssociated) && (&ExThread->Tcb == ExTimer->TimerApc.Thread)) {
+ RemoveEntryList(&ExTimer->ActiveTimerListEntry);
+ ExTimer->ApcAssociated = FALSE;
+ KeCancelTimer(&ExTimer->KeTimer);
+ KeRemoveQueueDpc(&ExTimer->TimerDpc);
+ KeRemoveQueueApc(&ExTimer->TimerApc);
+ Dereference = TRUE;
+
+ } else {
+ Dereference = FALSE;
+ }
+
+ ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql2);
+ ExReleaseSpinLock(&ExTimer->Lock, OldIrql1);
+ if (Dereference) {
+ ObDereferenceObject((PVOID)ExTimer);
+ }
+
+ ObDereferenceObject((PVOID)ExTimer);
+
+ //
+ // Raise IRQL to DISPATCH_LEVEL and reacquire active timer list
+ // spin lock.
+ //
+
+ ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql1);
+ NextEntry = ExThread->ActiveTimerListHead.Flink;
+ }
+
+ ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql1);
+ return;
+}
+
+NTSTATUS
+NtCreateTimer (
+ OUT PHANDLE TimerHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN TIMER_TYPE TimerType
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates an timer object and opens a handle to the object with
+ the specified desired access.
+
+Arguments:
+
+ TimerHandle - Supplies a pointer to a variable that will receive the
+ timer object handle.
+
+ DesiredAccess - Supplies the desired types of access for the timer object.
+
+ ObjectAttributes - Supplies a pointer to an object attributes structure.
+
+ TimerType - Supplies the type of the timer (autoclearing or notification).
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+
+ PETIMER ExTimer;
+ HANDLE Handle;
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ //
+ // Establish an exception handler, probe the output handle address, and
+ // attempt to create a timer object. If the probe fails, then return the
+ // exception code as the service status. Otherwise return the status value
+ // returned by the object insertion routine.
+ //
+
+ try {
+
+ //
+ // Get previous processor mode and probe output handle address if
+ // necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ ProbeForWriteHandle(TimerHandle);
+ }
+
+ //
+ // Check argument validity.
+ //
+
+ if ((TimerType != NotificationTimer) &&
+ (TimerType != SynchronizationTimer)) {
+ return STATUS_INVALID_PARAMETER_4;
+ }
+
+ //
+ // Allocate timer object.
+ //
+
+ Status = ObCreateObject(PreviousMode,
+ ExTimerObjectType,
+ ObjectAttributes,
+ PreviousMode,
+ NULL,
+ sizeof(ETIMER),
+ 0,
+ 0,
+ (PVOID *)&ExTimer);
+
+ //
+ // If the timer object was successfully allocated, then initialize the
+ // timer object and attempt to insert the time object in the current
+ // process' handle table.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ KeInitializeDpc(&ExTimer->TimerDpc,
+ ExpTimerDpcRoutine,
+ (PVOID)ExTimer);
+
+ KeInitializeTimerEx(&ExTimer->KeTimer, TimerType);
+ KeInitializeSpinLock(&ExTimer->Lock);
+ ExTimer->ApcAssociated = FALSE;
+ Status = ObInsertObject((PVOID)ExTimer,
+ NULL,
+ DesiredAccess,
+ 0,
+ (PVOID *)NULL,
+ &Handle);
+
+ //
+ // If the timer object was successfully inserted in the current
+ // process' handle table, then attempt to write the timer object
+ // handle value. If the write attempt fails, then do not report
+ // an error. When the caller attempts to access the handle value,
+ // an access violation will occur.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ try {
+ *TimerHandle = Handle;
+ } except(ExSystemExceptionFilter()) {
+ }
+ }
+ }
+
+ //
+ // If an exception occurs during the probe of the output handle address,
+ // then always handle the exception and return the exception code as the
+ // status value.
+ //
+
+ } except(ExSystemExceptionFilter()) {
+ return GetExceptionCode();
+ }
+
+ //
+ // Return service status.
+ //
+
+ return Status;
+}
+
+NTSTATUS
+NtOpenTimer (
+ OUT PHANDLE TimerHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens a handle to an timer object with the specified
+ desired access.
+
+Arguments:
+
+ TimerHandle - Supplies a pointer to a variable that will receive the
+ timer object handle.
+
+ DesiredAccess - Supplies the desired types of access for the timer object.
+
+ ObjectAttributes - Supplies a pointer to an object attributes structure.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+
+ HANDLE Handle;
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ //
+ // Establish an exception handler, probe the output handle address, and
+ // attempt to open a timer object. If the probe fails, then return the
+ // exception code as the service status. Otherwise return the status value
+ // returned by the open object routine.
+ //
+
+ try {
+
+ //
+ // Get previous processor mode and probe output handle address if
+ // necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ ProbeForWriteHandle(TimerHandle);
+ }
+
+ //
+ // Open handle to the timer object with the specified desired access.
+ //
+
+ Status = ObOpenObjectByName(ObjectAttributes,
+ ExTimerObjectType,
+ PreviousMode,
+ NULL,
+ DesiredAccess,
+ NULL,
+ &Handle);
+
+ //
+ // If the open was successful, then attempt to write the timer object
+ // handle value. If the write attempt fails, then do not report an
+ // error. When the caller attempts to access the handle value, an
+ // access violation will occur.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ try {
+ *TimerHandle = Handle;
+
+ } except(ExSystemExceptionFilter()) {
+ }
+ }
+
+ //
+ // If an exception occurs during the probe of the output handle address,
+ // then always handle the exception and return the exception code as the
+ // status value.
+ //
+
+ } except(ExSystemExceptionFilter()) {
+ return GetExceptionCode();
+ }
+
+ //
+ // Return service status.
+ //
+
+ return Status;
+}
+
+NTSTATUS
+NtCancelTimer (
+ IN HANDLE TimerHandle,
+ OUT PBOOLEAN CurrentState OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function cancels a timer object.
+
+Arguments:
+
+ TimerHandle - Supplies a handle to an timer object.
+
+ CurrentState - Supplies an optional pointer to a variable that will
+ receive the current state of the timer object.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+
+ BOOLEAN Dereference;
+ PETHREAD ExThread;
+ PETIMER ExTimer;
+ KIRQL OldIrql1;
+ KIRQL OldIrql2;
+ KPROCESSOR_MODE PreviousMode;
+ BOOLEAN State;
+ NTSTATUS Status;
+
+ //
+ // Establish an exception handler, probe the current state address if
+ // specified, reference the timer object, and cancel the timer object.
+ // If the probe fails, then return the exception code as the service
+ // status. Otherwise return the status value returned by the reference
+ // object by handle routine.
+ //
+
+ try {
+
+ //
+ // Get previous processor mode and probe current state address if
+ // necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if ((PreviousMode != KernelMode) && (ARGUMENT_PRESENT(CurrentState))) {
+ ProbeForWriteBoolean(CurrentState);
+ }
+
+ //
+ // Reference timer object by handle.
+ //
+
+ Status = ObReferenceObjectByHandle(TimerHandle,
+ TIMER_MODIFY_STATE,
+ ExTimerObjectType,
+ PreviousMode,
+ (PVOID *)&ExTimer,
+ NULL);
+
+ //
+ // If the reference was successful, then cancel the timer object,
+ // dereference the timer object, and write the current state value
+ // if specified. If the write attempt fails, then do not report an
+ // error. When the caller attempts to access the current state value,
+ // an access violation will occur.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1);
+ if (ExTimer->ApcAssociated) {
+ ExThread = CONTAINING_RECORD(ExTimer->TimerApc.Thread, ETHREAD, Tcb);
+ ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql2);
+ RemoveEntryList(&ExTimer->ActiveTimerListEntry);
+ ExTimer->ApcAssociated = FALSE;
+ ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql2);
+ KeCancelTimer(&ExTimer->KeTimer);
+ KeRemoveQueueDpc(&ExTimer->TimerDpc);
+ KeRemoveQueueApc(&ExTimer->TimerApc);
+ Dereference = TRUE;
+
+ } else {
+ KeCancelTimer(&ExTimer->KeTimer);
+ Dereference = FALSE;
+ }
+
+ ExReleaseSpinLock(&ExTimer->Lock, OldIrql1);
+ if (Dereference) {
+ ObDereferenceObject((PVOID)ExTimer);
+ }
+
+ //
+ // Read current state of timer, dereference timer object, and set
+ // current state.
+ //
+
+ State = KeReadStateTimer(&ExTimer->KeTimer);
+ ObDereferenceObject(ExTimer);
+ if (ARGUMENT_PRESENT(CurrentState)) {
+ try {
+ *CurrentState = State;
+
+ } except(ExSystemExceptionFilter()) {
+ }
+ }
+ }
+
+ //
+ // If an exception occurs during the probe of the current state address,
+ // then always handle the exception and return the exception code as the
+ // status value.
+ //
+
+ } except(ExSystemExceptionFilter()) {
+ return GetExceptionCode();
+ }
+
+ //
+ // Return service status.
+ //
+
+ return Status;
+}
+
+NTSTATUS
+NtQueryTimer (
+ IN HANDLE TimerHandle,
+ IN TIMER_INFORMATION_CLASS TimerInformationClass,
+ OUT PVOID TimerInformation,
+ IN ULONG TimerInformationLength,
+ OUT PULONG ReturnLength OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function queries the state of an timer object and returns the
+ requested information in the specified record structure.
+
+Arguments:
+
+ TimerHandle - Supplies a handle to an timer object.
+
+ TimerInformationClass - Supplies the class of information being requested.
+
+ TimerInformation - Supplies a pointer to a record that is to receive the
+ requested information.
+
+ TimerInformationLength - Supplies the length of the record that is to
+ receive the requested information.
+
+ ReturnLength - Supplies an optional pointer to a variable that is to
+ receive the actual length of information that is returned.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+
+ PETIMER ExTimer;
+ PKTIMER KeTimer;
+ KPROCESSOR_MODE PreviousMode;
+ BOOLEAN State;
+ NTSTATUS Status;
+ LARGE_INTEGER TimeToGo;
+
+ //
+ // Establish an exception handler, probe the output arguments, reference
+ // the timer object, and return the specified information. If the probe
+ // fails, then return the exception code as the service status. Otherwise
+ // return the status value returned by the reference object by handle
+ // routine.
+ //
+
+ try {
+
+ //
+ // Get previous processor mode and probe output arguments if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ ProbeForWrite(TimerInformation,
+ sizeof(TIMER_BASIC_INFORMATION),
+ sizeof(ULONG));
+
+ if (ARGUMENT_PRESENT(ReturnLength)) {
+ ProbeForWriteUlong(ReturnLength);
+ }
+ }
+
+ //
+ // Check argument validity.
+ //
+
+ if (TimerInformationClass != TimerBasicInformation) {
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (TimerInformationLength != sizeof(TIMER_BASIC_INFORMATION)) {
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Reference timer object by handle.
+ //
+
+ Status = ObReferenceObjectByHandle(TimerHandle,
+ TIMER_QUERY_STATE,
+ ExTimerObjectType,
+ PreviousMode,
+ (PVOID *)&ExTimer,
+ NULL);
+
+ //
+ // If the reference was successful, then read the current state,
+ // compute the time remaining, dereference the timer object, fill in
+ // the information structure, and return the length of the information
+ // structure if specified. If the write of the time information or the
+ // return length fails, then do not report an error. When the caller
+ // accesses the information structure or the length, an violation will
+ // occur.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ KeTimer = &ExTimer->KeTimer;
+ State = KeReadStateTimer(KeTimer);
+ KiQueryInterruptTime(&TimeToGo);
+ TimeToGo.QuadPart = KeTimer->DueTime.QuadPart - TimeToGo.QuadPart;
+ ObDereferenceObject(ExTimer);
+ try {
+ ((PTIMER_BASIC_INFORMATION)TimerInformation)->TimerState = State;
+ ((PTIMER_BASIC_INFORMATION)TimerInformation)->RemainingTime = TimeToGo;
+ if (ARGUMENT_PRESENT(ReturnLength)) {
+ *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
+ }
+
+ } except(ExSystemExceptionFilter()) {
+ }
+ }
+
+ //
+ // If an exception occurs during the probe of the current state address,
+ // then always handle the exception and return the exception code as the
+ // status value.
+ //
+
+ } except(ExSystemExceptionFilter()) {
+ return GetExceptionCode();
+ }
+
+ //
+ // Return service status.
+ //
+
+ return Status;
+}
+
+NTSTATUS
+NtSetTimer (
+ IN HANDLE TimerHandle,
+ IN PLARGE_INTEGER DueTime,
+ IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
+ IN PVOID TimerContext OPTIONAL,
+ IN BOOLEAN ResumeTimer,
+ IN LONG Period OPTIONAL,
+ OUT PBOOLEAN PreviousState OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets an timer object to a Not-Signaled state and sets the timer
+ to expire at the specified time.
+
+Arguments:
+
+ TimerHandle - Supplies a handle to an timer object.
+
+ DueTime - Supplies a pointer to absolute of relative time at which the
+ timer is to expire.
+
+ TimerApcRoutine - Supplies an optional pointer to a function which is to
+ be executed when the timer expires. If this parameter is not specified,
+ then the TimerContext parameter is ignored.
+
+ TimerContext - Supplies an optional pointer to an arbitrary data structure
+ that will be passed to the function specified by the TimerApcRoutine
+ parameter. This parameter is ignored if the TimerApcRoutine parameter
+ is not specified.
+
+ ResumeTimer - Supplies a boolean value that specifies whether the timer
+ resumes computer operation if suspended.
+
+ Period - Supplies an optional repetitive period for the timer.
+
+ PreviousState - Supplies an optional pointer to a variable that will
+ receive the previous state of the timer object.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+
+ BOOLEAN AssociatedApc;
+ BOOLEAN Dereference;
+ PETHREAD ExThread;
+ PETIMER ExTimer;
+ LARGE_INTEGER ExpirationTime;
+ KIRQL OldIrql1;
+ KIRQL OldIrql2;
+ KPROCESSOR_MODE PreviousMode;
+ BOOLEAN State;
+ NTSTATUS Status;
+
+ //
+ // Establish an exception handler, probe the due time and previous state
+ // address if specified, reference the timer object, and set the timer
+ // object. If the probe fails, then return the exception code as the
+ // service status. Otherwise return the status value returned by the
+ // reference object by handle routine.
+ //
+
+ try {
+
+ //
+ // Get previous processor mode and probe previous state address
+ // if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ ProbeForWriteBoolean(PreviousState);
+ }
+
+ ProbeForRead(DueTime, sizeof(LARGE_INTEGER), sizeof(ULONG));
+ }
+
+ //
+ // Check argument validity.
+ //
+
+ if (Period < 0) {
+ return STATUS_INVALID_PARAMETER_6;
+ }
+
+ //
+ // Capture the expiration time.
+ //
+
+ ExpirationTime = *DueTime;
+
+ //
+ // Reference timer object by handle.
+ //
+
+ Status = ObReferenceObjectByHandle(TimerHandle,
+ TIMER_MODIFY_STATE,
+ ExTimerObjectType,
+ PreviousMode,
+ (PVOID *)&ExTimer,
+ NULL);
+
+ //
+ // If this ResumeTimer flag is set, return the appropiate informational
+ // success status code.
+ //
+
+ if (NT_SUCCESS(Status) && ResumeTimer) {
+ Status = STATUS_TIMER_RESUME_IGNORED;
+ }
+
+ //
+ // If the reference was successful, then cancel the timer object, set
+ // the timer object, dereference time object, and write the previous
+ // state value if specified. If the write of the previous state value
+ // fails, then do not report an error. When the caller attempts to
+ // access the previous state value, an access violation will occur.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1);
+ if (ExTimer->ApcAssociated) {
+ ExThread = CONTAINING_RECORD(ExTimer->TimerApc.Thread, ETHREAD, Tcb);
+ ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql2);
+ RemoveEntryList(&ExTimer->ActiveTimerListEntry);
+ ExTimer->ApcAssociated = FALSE;
+ ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql2);
+ KeCancelTimer(&ExTimer->KeTimer);
+ KeRemoveQueueDpc(&ExTimer->TimerDpc);
+ KeRemoveQueueApc(&ExTimer->TimerApc);
+ Dereference = TRUE;
+
+ } else {
+ KeCancelTimer(&ExTimer->KeTimer);
+ Dereference = FALSE;
+ }
+
+ //
+ // Read the current state of the timer.
+ //
+
+ State = KeReadStateTimer(&ExTimer->KeTimer);
+
+ //
+ // If an APC routine is specified, then initialize the APC, acquire the
+ // thread's active time list lock, insert the timer in the thread's
+ // active timer list, set the timer with an associated DPC, and set the
+ // associated APC flag TRUE. Otherwise set the timer without an associated
+ // DPC, and set the associated APC flag FALSE.
+ //
+
+ ExTimer->Period = Period;
+ if (ARGUMENT_PRESENT(TimerApcRoutine)) {
+ ExThread = PsGetCurrentThread();
+ KeInitializeApc(&ExTimer->TimerApc,
+ &ExThread->Tcb,
+ CurrentApcEnvironment,
+ ExpTimerApcRoutine,
+ (PKRUNDOWN_ROUTINE)NULL,
+ (PKNORMAL_ROUTINE)TimerApcRoutine,
+ PreviousMode,
+ TimerContext);
+
+ ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql2);
+ InsertTailList(&ExThread->ActiveTimerListHead,
+ &ExTimer->ActiveTimerListEntry);
+
+ ExTimer->ApcAssociated = TRUE;
+ ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql2);
+ KeSetTimerEx(&ExTimer->KeTimer,
+ ExpirationTime,
+ Period,
+ &ExTimer->TimerDpc);
+
+ AssociatedApc = TRUE;
+
+ } else {
+ KeSetTimerEx(&ExTimer->KeTimer,
+ ExpirationTime,
+ Period,
+ NULL);
+
+ AssociatedApc = FALSE;
+ }
+
+ ExReleaseSpinLock(&ExTimer->Lock, OldIrql1);
+
+ //
+ // Dereference the object as appropriate.
+ //
+
+ if (Dereference) {
+ ObDereferenceObject((PVOID)ExTimer);
+ }
+
+ if (AssociatedApc == FALSE) {
+ ObDereferenceObject((PVOID)ExTimer);
+ }
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ try {
+ *PreviousState = State;
+
+ } except(ExSystemExceptionFilter()) {
+ }
+ }
+ }
+
+ //
+ // If an exception occurs during the probe of the current state address,
+ // then always handle the exception and return the exception code as the
+ // status value.
+ //
+
+ } except(ExSystemExceptionFilter()) {
+ return GetExceptionCode();
+ }
+
+ //
+ // Return service status.
+ //
+
+ return Status;
+}