Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1306 lines
35 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. timer.c
  5. Abstract:
  6. This module implements the executive timer object. Functions are provided
  7. to create, open, cancel, set, and query timer objects.
  8. Author:
  9. David N. Cutler (davec) 12-May-1989
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "exp.h"
  15. //
  16. // Executive timer object structure definition.
  17. //
  18. typedef struct _ETIMER {
  19. KTIMER KeTimer;
  20. KAPC TimerApc;
  21. KDPC TimerDpc;
  22. LIST_ENTRY ActiveTimerListEntry;
  23. KSPIN_LOCK Lock;
  24. LONG Period;
  25. BOOLEAN ApcAssociated;
  26. BOOLEAN WakeTimer;
  27. LIST_ENTRY WakeTimerListEntry;
  28. } ETIMER, *PETIMER;
  29. //
  30. // List of all timers which are set to wake
  31. //
  32. KSPIN_LOCK ExpWakeTimerListLock;
  33. LIST_ENTRY ExpWakeTimerList;
  34. //
  35. // Address of timer object type descriptor.
  36. //
  37. POBJECT_TYPE ExTimerObjectType;
  38. //
  39. // Structure that describes the mapping of generic access rights to object
  40. // specific access rights for timer objects.
  41. //
  42. #ifdef ALLOC_DATA_PRAGMA
  43. #pragma const_seg("INITCONST")
  44. #endif
  45. const GENERIC_MAPPING ExpTimerMapping = {
  46. STANDARD_RIGHTS_READ |
  47. TIMER_QUERY_STATE,
  48. STANDARD_RIGHTS_WRITE |
  49. TIMER_MODIFY_STATE,
  50. STANDARD_RIGHTS_EXECUTE |
  51. SYNCHRONIZE,
  52. TIMER_ALL_ACCESS
  53. };
  54. #ifdef ALLOC_DATA_PRAGMA
  55. #pragma const_seg()
  56. #endif
  57. #ifdef ALLOC_PRAGMA
  58. #pragma alloc_text(INIT, ExpTimerInitialization)
  59. #pragma alloc_text(PAGE, NtCreateTimer)
  60. #pragma alloc_text(PAGE, NtOpenTimer)
  61. #pragma alloc_text(PAGE, NtQueryTimer)
  62. #pragma alloc_text(PAGELK, ExGetNextWakeTime)
  63. #endif
  64. VOID
  65. ExpTimerApcRoutine (
  66. IN PKAPC Apc,
  67. IN PKNORMAL_ROUTINE *NormalRoutine,
  68. IN PVOID *NormalContext,
  69. IN PVOID *SystemArgument1,
  70. IN PVOID *SystemArgument2
  71. )
  72. /*++
  73. Routine Description:
  74. This function is the special APC routine that is called to remove
  75. a timer from the current thread's active timer list.
  76. Arguments:
  77. Apc - Supplies a pointer to the APC object used to invoke this routine.
  78. NormalRoutine - Supplies a pointer to a pointer to the normal routine
  79. function that was specified when the APC was initialized.
  80. NormalContext - Supplies a pointer to a pointer to an arbitrary data
  81. structure that was specified when the APC was initialized.
  82. SystemArgument1, SystemArgument2 - Supplies a set of two pointers to
  83. two arguments that contain untyped data.
  84. Return Value:
  85. None.
  86. --*/
  87. {
  88. BOOLEAN Dereference;
  89. PETHREAD ExThread;
  90. PETIMER ExTimer;
  91. KIRQL OldIrql1;
  92. UNREFERENCED_PARAMETER (NormalContext);
  93. UNREFERENCED_PARAMETER (SystemArgument1);
  94. UNREFERENCED_PARAMETER (SystemArgument2);
  95. //
  96. // Get address of executive timer object and the current thread object.
  97. //
  98. ExThread = PsGetCurrentThread();
  99. ExTimer = CONTAINING_RECORD(Apc, ETIMER, TimerApc);
  100. //
  101. // If the timer is still in the current thread's active timer list, then
  102. // remove it if it is not a periodic timer and set APC associated FALSE.
  103. // It is possible for the timer not to be in the current thread's active
  104. // timer list since the APC could have been delivered, and then another
  105. // thread could have set the timer again with another APC. This would
  106. // have caused the timer to be removed from the current thread's active
  107. // timer list.
  108. //
  109. // N. B. The spin locks for the timer and the active timer list must be
  110. // acquired in the order: 1) timer lock, 2) thread list lock.
  111. //
  112. Dereference = FALSE;
  113. ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1);
  114. ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock);
  115. if ((ExTimer->ApcAssociated) && (&ExThread->Tcb == ExTimer->TimerApc.Thread)) {
  116. if (ExTimer->Period == 0) {
  117. RemoveEntryList(&ExTimer->ActiveTimerListEntry);
  118. ExTimer->ApcAssociated = FALSE;
  119. Dereference = TRUE;
  120. }
  121. } else {
  122. *NormalRoutine = (PKNORMAL_ROUTINE)NULL;
  123. }
  124. ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock);
  125. ExReleaseSpinLock(&ExTimer->Lock, OldIrql1);
  126. if (Dereference) {
  127. ObDereferenceObject((PVOID)ExTimer);
  128. }
  129. return;
  130. }
  131. VOID
  132. ExpTimerDpcRoutine (
  133. IN PKDPC Dpc,
  134. IN PVOID DeferredContext,
  135. IN PVOID SystemArgument1,
  136. IN PVOID SystemArgument2
  137. )
  138. /*++
  139. Routine Description:
  140. This function is the DPC routine that is called when a timer expires that
  141. has an associated APC routine. Its function is to insert the associated
  142. APC into the target thread's APC queue.
  143. Arguments:
  144. Dpc - Supplies a pointer to a control object of type DPC.
  145. DeferredContext - Supplies a pointer to the executive timer that contains
  146. the DPC that caused this routine to be executed.
  147. SystemArgument1, SystemArgument2 - Supplies values that are not used by
  148. this routine.
  149. Return Value:
  150. None.
  151. --*/
  152. {
  153. PETIMER ExTimer;
  154. PKTIMER KeTimer;
  155. KIRQL OldIrql;
  156. UNREFERENCED_PARAMETER (Dpc);
  157. //
  158. // Get address of executive and kernel timer objects.
  159. //
  160. ExTimer = (PETIMER)DeferredContext;
  161. KeTimer = &ExTimer->KeTimer;
  162. //
  163. // If there is still an APC associated with the timer, then insert the APC
  164. // in target thread's APC queue. It is possible that the timer does not
  165. // have an associated APC. This can happen when the timer is set to expire
  166. // by a thread running on another processor just after the DPC has been
  167. // removed from the DPC queue, but before it has acquired the timer related
  168. // spin lock.
  169. //
  170. ExAcquireSpinLock(&ExTimer->Lock, &OldIrql);
  171. if (ExTimer->ApcAssociated) {
  172. KeInsertQueueApc(&ExTimer->TimerApc,
  173. SystemArgument1,
  174. SystemArgument2,
  175. TIMER_APC_INCREMENT);
  176. }
  177. ExReleaseSpinLock(&ExTimer->Lock, OldIrql);
  178. return;
  179. }
  180. static VOID
  181. ExpDeleteTimer (
  182. IN PVOID Object
  183. )
  184. /*++
  185. Routine Description:
  186. This function is the delete routine for timer objects. Its function is
  187. to cancel the timer and free the spin lock associated with a timer.
  188. Arguments:
  189. Object - Supplies a pointer to an executive timer object.
  190. Return Value:
  191. None.
  192. --*/
  193. {
  194. PETIMER ExTimer;
  195. KIRQL OldIrql;
  196. ExTimer = (PETIMER) Object;
  197. //
  198. // Remove from wake list
  199. //
  200. if (ExTimer->WakeTimerListEntry.Flink) {
  201. ExAcquireSpinLock(&ExpWakeTimerListLock, &OldIrql);
  202. if (ExTimer->WakeTimerListEntry.Flink) {
  203. RemoveEntryList(&ExTimer->WakeTimerListEntry);
  204. ExTimer->WakeTimerListEntry.Flink = NULL;
  205. }
  206. ExReleaseSpinLock(&ExpWakeTimerListLock, OldIrql);
  207. }
  208. //
  209. // Cancel the timer and free the spin lock associated with the timer.
  210. //
  211. KeCancelTimer(&ExTimer->KeTimer);
  212. return;
  213. }
  214. BOOLEAN
  215. ExpTimerInitialization (
  216. )
  217. /*++
  218. Routine Description:
  219. This function creates the timer object type descriptor at system
  220. initialization and stores the address of the object type descriptor
  221. in local static storage.
  222. Arguments:
  223. None.
  224. Return Value:
  225. A value of TRUE is returned if the timer object type descriptor is
  226. successfully initialized. Otherwise a value of FALSE is returned.
  227. --*/
  228. {
  229. OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
  230. NTSTATUS Status;
  231. UNICODE_STRING TypeName;
  232. KeInitializeSpinLock (&ExpWakeTimerListLock);
  233. InitializeListHead (&ExpWakeTimerList);
  234. //
  235. // Initialize string descriptor.
  236. //
  237. RtlInitUnicodeString(&TypeName, L"Timer");
  238. //
  239. // Create timer object type descriptor.
  240. //
  241. RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
  242. ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
  243. ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
  244. ObjectTypeInitializer.GenericMapping = ExpTimerMapping;
  245. ObjectTypeInitializer.PoolType = NonPagedPool;
  246. ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETIMER);
  247. ObjectTypeInitializer.ValidAccessMask = TIMER_ALL_ACCESS;
  248. ObjectTypeInitializer.DeleteProcedure = ExpDeleteTimer;
  249. Status = ObCreateObjectType(&TypeName,
  250. &ObjectTypeInitializer,
  251. (PSECURITY_DESCRIPTOR)NULL,
  252. &ExTimerObjectType);
  253. //
  254. // If the time object type descriptor was successfully created, then
  255. // return a value of TRUE. Otherwise return a value of FALSE.
  256. //
  257. return (BOOLEAN)(NT_SUCCESS(Status));
  258. }
  259. VOID
  260. ExTimerRundown (
  261. )
  262. /*++
  263. Routine Description:
  264. This function is called when a thread is about to be terminated to
  265. process the active timer list. It is assumed that APC's have been
  266. disabled for the subject thread, thus this code cannot be interrupted
  267. to execute an APC for the current thread.
  268. Arguments:
  269. None.
  270. Return Value:
  271. None.
  272. --*/
  273. {
  274. BOOLEAN Dereference;
  275. PETHREAD ExThread;
  276. PETIMER ExTimer;
  277. PLIST_ENTRY NextEntry;
  278. KIRQL OldIrql1;
  279. //
  280. // Process each entry in the active timer list.
  281. //
  282. ExThread = PsGetCurrentThread();
  283. ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql1);
  284. NextEntry = ExThread->ActiveTimerListHead.Flink;
  285. while (NextEntry != &ExThread->ActiveTimerListHead) {
  286. ExTimer = CONTAINING_RECORD(NextEntry, ETIMER, ActiveTimerListEntry);
  287. //
  288. // Increment the reference count on the object so that it cannot be
  289. // deleted, and then drop the active timer list lock.
  290. //
  291. // N. B. The object reference cannot fail and will acquire no mutexes.
  292. //
  293. ObReferenceObject(ExTimer);
  294. ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql1);
  295. //
  296. // Acquire the timer spin lock and reacquire the active time list spin
  297. // lock. If the timer is still in the current thread's active timer
  298. // list, then cancel the timer, remove the timer's DPC from the DPC
  299. // queue, remove the timer's APC from the APC queue, remove the timer
  300. // from the thread's active timer list, and set the associate APC
  301. // flag FALSE.
  302. //
  303. // N. B. The spin locks for the timer and the active timer list must be
  304. // acquired in the order: 1) timer lock, 2) thread list lock.
  305. //
  306. ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1);
  307. ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock);
  308. if ((ExTimer->ApcAssociated) && (&ExThread->Tcb == ExTimer->TimerApc.Thread)) {
  309. RemoveEntryList(&ExTimer->ActiveTimerListEntry);
  310. ExTimer->ApcAssociated = FALSE;
  311. KeCancelTimer(&ExTimer->KeTimer);
  312. KeRemoveQueueDpc(&ExTimer->TimerDpc);
  313. KeRemoveQueueApc(&ExTimer->TimerApc);
  314. Dereference = TRUE;
  315. } else {
  316. Dereference = FALSE;
  317. }
  318. ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock);
  319. ExReleaseSpinLock(&ExTimer->Lock, OldIrql1);
  320. if (Dereference) {
  321. ObDereferenceObject((PVOID)ExTimer);
  322. }
  323. ObDereferenceObject((PVOID)ExTimer);
  324. //
  325. // Raise IRQL to DISPATCH_LEVEL and reacquire active timer list
  326. // spin lock.
  327. //
  328. ExAcquireSpinLock(&ExThread->ActiveTimerListLock, &OldIrql1);
  329. NextEntry = ExThread->ActiveTimerListHead.Flink;
  330. }
  331. ExReleaseSpinLock(&ExThread->ActiveTimerListLock, OldIrql1);
  332. return;
  333. }
  334. NTSTATUS
  335. NtCreateTimer (
  336. OUT PHANDLE TimerHandle,
  337. IN ACCESS_MASK DesiredAccess,
  338. IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
  339. IN TIMER_TYPE TimerType
  340. )
  341. /*++
  342. Routine Description:
  343. This function creates an timer object and opens a handle to the object with
  344. the specified desired access.
  345. Arguments:
  346. TimerHandle - Supplies a pointer to a variable that will receive the
  347. timer object handle.
  348. DesiredAccess - Supplies the desired types of access for the timer object.
  349. ObjectAttributes - Supplies a pointer to an object attributes structure.
  350. TimerType - Supplies the type of the timer (autoclearing or notification).
  351. Return Value:
  352. NTSTATUS.
  353. --*/
  354. {
  355. PETIMER ExTimer;
  356. HANDLE Handle;
  357. KPROCESSOR_MODE PreviousMode;
  358. NTSTATUS Status;
  359. //
  360. // Establish an exception handler, probe the output handle address, and
  361. // attempt to create a timer object. If the probe fails, then return the
  362. // exception code as the service status. Otherwise return the status value
  363. // returned by the object insertion routine.
  364. //
  365. //
  366. // Get previous processor mode and probe output handle address if
  367. // necessary.
  368. //
  369. PreviousMode = KeGetPreviousMode();
  370. if (PreviousMode != KernelMode) {
  371. try {
  372. ProbeForWriteHandle(TimerHandle);
  373. } except(ExSystemExceptionFilter()) {
  374. return GetExceptionCode();
  375. }
  376. }
  377. //
  378. // Check argument validity.
  379. //
  380. if ((TimerType != NotificationTimer) &&
  381. (TimerType != SynchronizationTimer)) {
  382. return STATUS_INVALID_PARAMETER_4;
  383. }
  384. //
  385. // Allocate timer object.
  386. //
  387. Status = ObCreateObject(PreviousMode,
  388. ExTimerObjectType,
  389. ObjectAttributes,
  390. PreviousMode,
  391. NULL,
  392. sizeof(ETIMER),
  393. 0,
  394. 0,
  395. (PVOID *)&ExTimer);
  396. //
  397. // If the timer object was successfully allocated, then initialize the
  398. // timer object and attempt to insert the time object in the current
  399. // process' handle table.
  400. //
  401. if (NT_SUCCESS(Status)) {
  402. KeInitializeDpc(&ExTimer->TimerDpc,
  403. ExpTimerDpcRoutine,
  404. (PVOID)ExTimer);
  405. KeInitializeTimerEx(&ExTimer->KeTimer, TimerType);
  406. KeInitializeSpinLock(&ExTimer->Lock);
  407. ExTimer->ApcAssociated = FALSE;
  408. ExTimer->WakeTimer = FALSE;
  409. ExTimer->WakeTimerListEntry.Flink = NULL;
  410. Status = ObInsertObject((PVOID)ExTimer,
  411. NULL,
  412. DesiredAccess,
  413. 0,
  414. (PVOID *)NULL,
  415. &Handle);
  416. //
  417. // If the timer object was successfully inserted in the current
  418. // process' handle table, then attempt to write the timer object
  419. // handle value. If the write attempt fails, then do not report
  420. // an error. When the caller attempts to access the handle value,
  421. // an access violation will occur.
  422. //
  423. if (NT_SUCCESS(Status)) {
  424. if (PreviousMode != KernelMode) {
  425. try {
  426. *TimerHandle = Handle;
  427. } except(ExSystemExceptionFilter()) {
  428. NOTHING;
  429. }
  430. }
  431. else {
  432. *TimerHandle = Handle;
  433. }
  434. }
  435. }
  436. //
  437. // Return service status.
  438. //
  439. return Status;
  440. }
  441. NTSTATUS
  442. NtOpenTimer (
  443. OUT PHANDLE TimerHandle,
  444. IN ACCESS_MASK DesiredAccess,
  445. IN POBJECT_ATTRIBUTES ObjectAttributes
  446. )
  447. /*++
  448. Routine Description:
  449. This function opens a handle to an timer object with the specified
  450. desired access.
  451. Arguments:
  452. TimerHandle - Supplies a pointer to a variable that will receive the
  453. timer object handle.
  454. DesiredAccess - Supplies the desired types of access for the timer object.
  455. ObjectAttributes - Supplies a pointer to an object attributes structure.
  456. Return Value:
  457. NTSTATUS.
  458. --*/
  459. {
  460. HANDLE Handle;
  461. KPROCESSOR_MODE PreviousMode;
  462. NTSTATUS Status;
  463. //
  464. // Establish an exception handler, probe the output handle address, and
  465. // attempt to open a timer object. If the probe fails, then return the
  466. // exception code as the service status. Otherwise return the status value
  467. // returned by the open object routine.
  468. //
  469. //
  470. // Get previous processor mode and probe output handle address if
  471. // necessary.
  472. //
  473. PreviousMode = KeGetPreviousMode();
  474. if (PreviousMode != KernelMode) {
  475. try {
  476. ProbeForWriteHandle(TimerHandle);
  477. } except(ExSystemExceptionFilter()) {
  478. return GetExceptionCode();
  479. }
  480. }
  481. //
  482. // Open handle to the timer object with the specified desired access.
  483. //
  484. Status = ObOpenObjectByName(ObjectAttributes,
  485. ExTimerObjectType,
  486. PreviousMode,
  487. NULL,
  488. DesiredAccess,
  489. NULL,
  490. &Handle);
  491. //
  492. // If the open was successful, then attempt to write the timer object
  493. // handle value. If the write attempt fails, then do not report an
  494. // error. When the caller attempts to access the handle value, an
  495. // access violation will occur.
  496. //
  497. if (NT_SUCCESS(Status)) {
  498. if (PreviousMode != KernelMode) {
  499. try {
  500. *TimerHandle = Handle;
  501. } except(ExSystemExceptionFilter()) {
  502. NOTHING;
  503. }
  504. }
  505. else {
  506. *TimerHandle = Handle;
  507. }
  508. }
  509. //
  510. // Return service status.
  511. //
  512. return Status;
  513. }
  514. NTSTATUS
  515. NtCancelTimer (
  516. IN HANDLE TimerHandle,
  517. OUT PBOOLEAN CurrentState OPTIONAL
  518. )
  519. /*++
  520. Routine Description:
  521. This function cancels a timer object.
  522. Arguments:
  523. TimerHandle - Supplies a handle to an timer object.
  524. CurrentState - Supplies an optional pointer to a variable that will
  525. receive the current state of the timer object.
  526. Return Value:
  527. NTSTATUS.
  528. --*/
  529. {
  530. BOOLEAN Dereference;
  531. PETHREAD ExThread;
  532. PETIMER ExTimer;
  533. KIRQL OldIrql1;
  534. KPROCESSOR_MODE PreviousMode;
  535. BOOLEAN State;
  536. NTSTATUS Status;
  537. //
  538. // Establish an exception handler, probe the current state address if
  539. // specified, reference the timer object, and cancel the timer object.
  540. // If the probe fails, then return the exception code as the service
  541. // status. Otherwise return the status value returned by the reference
  542. // object by handle routine.
  543. //
  544. //
  545. // Get previous processor mode and probe current state address if
  546. // necessary.
  547. //
  548. PreviousMode = KeGetPreviousMode();
  549. if ((ARGUMENT_PRESENT(CurrentState)) && (PreviousMode != KernelMode)) {
  550. try {
  551. ProbeForWriteBoolean(CurrentState);
  552. } except(ExSystemExceptionFilter()) {
  553. return GetExceptionCode();
  554. }
  555. }
  556. //
  557. // Reference timer object by handle.
  558. //
  559. Status = ObReferenceObjectByHandle(TimerHandle,
  560. TIMER_MODIFY_STATE,
  561. ExTimerObjectType,
  562. PreviousMode,
  563. (PVOID *)&ExTimer,
  564. NULL);
  565. //
  566. // If the reference was successful, then cancel the timer object,
  567. // dereference the timer object, and write the current state value
  568. // if specified. If the write attempt fails, then do not report an
  569. // error. When the caller attempts to access the current state value,
  570. // an access violation will occur.
  571. //
  572. if (NT_SUCCESS(Status)) {
  573. ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1);
  574. if (ExTimer->ApcAssociated) {
  575. ExThread = CONTAINING_RECORD(ExTimer->TimerApc.Thread, ETHREAD, Tcb);
  576. ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock);
  577. RemoveEntryList(&ExTimer->ActiveTimerListEntry);
  578. ExTimer->ApcAssociated = FALSE;
  579. ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock);
  580. KeCancelTimer(&ExTimer->KeTimer);
  581. KeRemoveQueueDpc(&ExTimer->TimerDpc);
  582. KeRemoveQueueApc(&ExTimer->TimerApc);
  583. Dereference = TRUE;
  584. } else {
  585. KeCancelTimer(&ExTimer->KeTimer);
  586. Dereference = FALSE;
  587. }
  588. if (ExTimer->WakeTimerListEntry.Flink) {
  589. ExAcquireSpinLockAtDpcLevel(&ExpWakeTimerListLock);
  590. //
  591. // Check again as ExGetNextWakeTime might have removed it.
  592. //
  593. if (ExTimer->WakeTimerListEntry.Flink) {
  594. RemoveEntryList(&ExTimer->WakeTimerListEntry);
  595. ExTimer->WakeTimerListEntry.Flink = NULL;
  596. }
  597. ExReleaseSpinLockFromDpcLevel(&ExpWakeTimerListLock);
  598. }
  599. ExReleaseSpinLock(&ExTimer->Lock, OldIrql1);
  600. if (Dereference) {
  601. ObDereferenceObject((PVOID)ExTimer);
  602. }
  603. //
  604. // Read current state of timer, dereference timer object, and set
  605. // current state.
  606. //
  607. State = KeReadStateTimer(&ExTimer->KeTimer);
  608. ObDereferenceObject(ExTimer);
  609. if (ARGUMENT_PRESENT(CurrentState)) {
  610. if (PreviousMode != KernelMode) {
  611. try {
  612. *CurrentState = State;
  613. } except(ExSystemExceptionFilter()) {
  614. }
  615. }
  616. else {
  617. *CurrentState = State;
  618. }
  619. }
  620. }
  621. //
  622. // Return service status.
  623. //
  624. return Status;
  625. }
  626. NTSTATUS
  627. NtQueryTimer (
  628. IN HANDLE TimerHandle,
  629. IN TIMER_INFORMATION_CLASS TimerInformationClass,
  630. OUT PVOID TimerInformation,
  631. IN ULONG TimerInformationLength,
  632. OUT PULONG ReturnLength OPTIONAL
  633. )
  634. /*++
  635. Routine Description:
  636. This function queries the state of an timer object and returns the
  637. requested information in the specified record structure.
  638. Arguments:
  639. TimerHandle - Supplies a handle to an timer object.
  640. TimerInformationClass - Supplies the class of information being requested.
  641. TimerInformation - Supplies a pointer to a record that is to receive the
  642. requested information.
  643. TimerInformationLength - Supplies the length of the record that is to
  644. receive the requested information.
  645. ReturnLength - Supplies an optional pointer to a variable that is to
  646. receive the actual length of information that is returned.
  647. Return Value:
  648. NTSTATUS.
  649. --*/
  650. {
  651. PETIMER ExTimer;
  652. PKTIMER KeTimer;
  653. KPROCESSOR_MODE PreviousMode;
  654. BOOLEAN State;
  655. NTSTATUS Status;
  656. LARGE_INTEGER TimeToGo;
  657. //
  658. // Establish an exception handler, probe the output arguments, reference
  659. // the timer object, and return the specified information. If the probe
  660. // fails, then return the exception code as the service status. Otherwise
  661. // return the status value returned by the reference object by handle
  662. // routine.
  663. //
  664. //
  665. // Get previous processor mode and probe output arguments if necessary.
  666. //
  667. PreviousMode = KeGetPreviousMode();
  668. if (PreviousMode != KernelMode) {
  669. try {
  670. ProbeForWriteSmallStructure(TimerInformation,
  671. sizeof(TIMER_BASIC_INFORMATION),
  672. sizeof(ULONG));
  673. if (ARGUMENT_PRESENT(ReturnLength)) {
  674. ProbeForWriteUlong(ReturnLength);
  675. }
  676. } except(ExSystemExceptionFilter()) {
  677. return GetExceptionCode();
  678. }
  679. }
  680. //
  681. // Check argument validity.
  682. //
  683. if (TimerInformationClass != TimerBasicInformation) {
  684. return STATUS_INVALID_INFO_CLASS;
  685. }
  686. if (TimerInformationLength != sizeof(TIMER_BASIC_INFORMATION)) {
  687. return STATUS_INFO_LENGTH_MISMATCH;
  688. }
  689. //
  690. // Reference timer object by handle.
  691. //
  692. Status = ObReferenceObjectByHandle(TimerHandle,
  693. TIMER_QUERY_STATE,
  694. ExTimerObjectType,
  695. PreviousMode,
  696. (PVOID *)&ExTimer,
  697. NULL);
  698. //
  699. // If the reference was successful, then read the current state,
  700. // compute the time remaining, dereference the timer object, fill in
  701. // the information structure, and return the length of the information
  702. // structure if specified. If the write of the time information or the
  703. // return length fails, then do not report an error. When the caller
  704. // accesses the information structure or the length, an violation will
  705. // occur.
  706. //
  707. if (NT_SUCCESS(Status)) {
  708. KeTimer = &ExTimer->KeTimer;
  709. State = KeReadStateTimer(KeTimer);
  710. KiQueryInterruptTime(&TimeToGo);
  711. TimeToGo.QuadPart = KeTimer->DueTime.QuadPart - TimeToGo.QuadPart;
  712. ObDereferenceObject(ExTimer);
  713. if (PreviousMode != KernelMode) {
  714. try {
  715. ((PTIMER_BASIC_INFORMATION)TimerInformation)->TimerState = State;
  716. ((PTIMER_BASIC_INFORMATION)TimerInformation)->RemainingTime = TimeToGo;
  717. if (ARGUMENT_PRESENT(ReturnLength)) {
  718. *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
  719. }
  720. } except(ExSystemExceptionFilter()) {
  721. NOTHING;
  722. }
  723. }
  724. else {
  725. ((PTIMER_BASIC_INFORMATION)TimerInformation)->TimerState = State;
  726. ((PTIMER_BASIC_INFORMATION)TimerInformation)->RemainingTime = TimeToGo;
  727. if (ARGUMENT_PRESENT(ReturnLength)) {
  728. *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
  729. }
  730. }
  731. }
  732. //
  733. // Return service status.
  734. //
  735. return Status;
  736. }
  737. NTSTATUS
  738. NtSetTimer (
  739. IN HANDLE TimerHandle,
  740. IN PLARGE_INTEGER DueTime,
  741. IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
  742. IN PVOID TimerContext OPTIONAL,
  743. IN BOOLEAN WakeTimer,
  744. IN LONG Period OPTIONAL,
  745. OUT PBOOLEAN PreviousState OPTIONAL
  746. )
  747. /*++
  748. Routine Description:
  749. This function sets an timer object to a Not-Signaled state and sets the timer
  750. to expire at the specified time.
  751. Arguments:
  752. TimerHandle - Supplies a handle to an timer object.
  753. DueTime - Supplies a pointer to absolute of relative time at which the
  754. timer is to expire.
  755. TimerApcRoutine - Supplies an optional pointer to a function which is to
  756. be executed when the timer expires. If this parameter is not specified,
  757. then the TimerContext parameter is ignored.
  758. TimerContext - Supplies an optional pointer to an arbitrary data structure
  759. that will be passed to the function specified by the TimerApcRoutine
  760. parameter. This parameter is ignored if the TimerApcRoutine parameter
  761. is not specified.
  762. WakeTimer - Supplies a boolean value that specifies whether the timer
  763. wakes computer operation if sleeping
  764. Period - Supplies an optional repetitive period for the timer.
  765. PreviousState - Supplies an optional pointer to a variable that will
  766. receive the previous state of the timer object.
  767. Return Value:
  768. NTSTATUS.
  769. --*/
  770. {
  771. BOOLEAN AssociatedApc;
  772. BOOLEAN Dereference;
  773. PETHREAD ExThread;
  774. PETIMER ExTimer;
  775. LARGE_INTEGER ExpirationTime;
  776. KIRQL OldIrql1;
  777. KPROCESSOR_MODE PreviousMode;
  778. BOOLEAN State;
  779. NTSTATUS Status;
  780. //
  781. // Establish an exception handler, probe the due time and previous state
  782. // address if specified, reference the timer object, and set the timer
  783. // object. If the probe fails, then return the exception code as the
  784. // service status. Otherwise return the status value returned by the
  785. // reference object by handle routine.
  786. //
  787. //
  788. // Get previous processor mode and probe previous state address
  789. // if necessary.
  790. //
  791. PreviousMode = KeGetPreviousMode();
  792. if (PreviousMode != KernelMode) {
  793. try {
  794. if (ARGUMENT_PRESENT(PreviousState)) {
  795. ProbeForWriteBoolean(PreviousState);
  796. }
  797. ProbeForReadSmallStructure(DueTime, sizeof(LARGE_INTEGER), sizeof(ULONG));
  798. ExpirationTime = *DueTime;
  799. } except(ExSystemExceptionFilter()) {
  800. return GetExceptionCode();
  801. }
  802. }
  803. else {
  804. ExpirationTime = *DueTime;
  805. }
  806. //
  807. // Check argument validity.
  808. //
  809. if (Period < 0) {
  810. return STATUS_INVALID_PARAMETER_6;
  811. }
  812. //
  813. // Reference timer object by handle.
  814. //
  815. Status = ObReferenceObjectByHandle(TimerHandle,
  816. TIMER_MODIFY_STATE,
  817. ExTimerObjectType,
  818. PreviousMode,
  819. (PVOID *)&ExTimer,
  820. NULL);
  821. //
  822. // If this WakeTimer flag is set, return the appropiate informational
  823. // success status code.
  824. //
  825. if (NT_SUCCESS(Status) && WakeTimer && !PoWakeTimerSupported()) {
  826. Status = STATUS_TIMER_RESUME_IGNORED;
  827. }
  828. //
  829. // If the reference was successful, then cancel the timer object, set
  830. // the timer object, dereference time object, and write the previous
  831. // state value if specified. If the write of the previous state value
  832. // fails, then do not report an error. When the caller attempts to
  833. // access the previous state value, an access violation will occur.
  834. //
  835. if (NT_SUCCESS(Status)) {
  836. ExAcquireSpinLock(&ExTimer->Lock, &OldIrql1);
  837. if (ExTimer->ApcAssociated) {
  838. ExThread = CONTAINING_RECORD(ExTimer->TimerApc.Thread, ETHREAD, Tcb);
  839. ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock);
  840. RemoveEntryList(&ExTimer->ActiveTimerListEntry);
  841. ExTimer->ApcAssociated = FALSE;
  842. ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock);
  843. KeCancelTimer(&ExTimer->KeTimer);
  844. KeRemoveQueueDpc(&ExTimer->TimerDpc);
  845. KeRemoveQueueApc(&ExTimer->TimerApc);
  846. Dereference = TRUE;
  847. } else {
  848. KeCancelTimer(&ExTimer->KeTimer);
  849. Dereference = FALSE;
  850. }
  851. //
  852. // Read the current state of the timer.
  853. //
  854. State = KeReadStateTimer(&ExTimer->KeTimer);
  855. //
  856. // If this is a wake timer ensure it's on the wake timer list.
  857. //
  858. ExTimer->WakeTimer = WakeTimer;
  859. ExAcquireSpinLockAtDpcLevel(&ExpWakeTimerListLock);
  860. if (WakeTimer) {
  861. if (!ExTimer->WakeTimerListEntry.Flink) {
  862. InsertTailList(&ExpWakeTimerList, &ExTimer->WakeTimerListEntry);
  863. }
  864. } else {
  865. if (ExTimer->WakeTimerListEntry.Flink) {
  866. RemoveEntryList(&ExTimer->WakeTimerListEntry);
  867. ExTimer->WakeTimerListEntry.Flink = NULL;
  868. }
  869. }
  870. ExReleaseSpinLockFromDpcLevel(&ExpWakeTimerListLock);
  871. //
  872. // If an APC routine is specified, then initialize the APC, acquire the
  873. // thread's active time list lock, insert the timer in the thread's
  874. // active timer list, set the timer with an associated DPC, and set the
  875. // associated APC flag TRUE. Otherwise set the timer without an
  876. // associated DPC, and set the associated APC flag FALSE.
  877. //
  878. ExTimer->Period = Period;
  879. if (ARGUMENT_PRESENT((ULONG_PTR)TimerApcRoutine)) {
  880. ExThread = PsGetCurrentThread();
  881. KeInitializeApc(&ExTimer->TimerApc,
  882. &ExThread->Tcb,
  883. CurrentApcEnvironment,
  884. ExpTimerApcRoutine,
  885. (PKRUNDOWN_ROUTINE)NULL,
  886. (PKNORMAL_ROUTINE)TimerApcRoutine,
  887. PreviousMode,
  888. TimerContext);
  889. ExAcquireSpinLockAtDpcLevel(&ExThread->ActiveTimerListLock);
  890. InsertTailList(&ExThread->ActiveTimerListHead,
  891. &ExTimer->ActiveTimerListEntry);
  892. ExTimer->ApcAssociated = TRUE;
  893. ExReleaseSpinLockFromDpcLevel(&ExThread->ActiveTimerListLock);
  894. KeSetTimerEx(&ExTimer->KeTimer,
  895. ExpirationTime,
  896. Period,
  897. &ExTimer->TimerDpc);
  898. AssociatedApc = TRUE;
  899. } else {
  900. KeSetTimerEx(&ExTimer->KeTimer,
  901. ExpirationTime,
  902. Period,
  903. NULL);
  904. AssociatedApc = FALSE;
  905. }
  906. ExReleaseSpinLock(&ExTimer->Lock, OldIrql1);
  907. //
  908. // Dereference the object as appropriate.
  909. //
  910. if (Dereference) {
  911. ObDereferenceObject((PVOID)ExTimer);
  912. }
  913. if (AssociatedApc == FALSE) {
  914. ObDereferenceObject((PVOID)ExTimer);
  915. }
  916. if (ARGUMENT_PRESENT(PreviousState)) {
  917. if (PreviousMode != KernelMode) {
  918. try {
  919. *PreviousState = State;
  920. } except(ExSystemExceptionFilter()) {
  921. NOTHING;
  922. }
  923. }
  924. else {
  925. *PreviousState = State;
  926. }
  927. }
  928. }
  929. //
  930. // Return service status.
  931. //
  932. return Status;
  933. }
  934. VOID
  935. ExGetNextWakeTime (
  936. OUT PULONGLONG DueTime,
  937. OUT PTIME_FIELDS TimeFields,
  938. OUT PVOID *TimerObject
  939. )
  940. {
  941. PLIST_ENTRY Link;
  942. PETIMER ExTimer;
  943. PETIMER BestTimer;
  944. KIRQL OldIrql;
  945. ULONGLONG TimerDueTime;
  946. ULONGLONG BestDueTime;
  947. ULONGLONG InterruptTime;
  948. LARGE_INTEGER SystemTime;
  949. LARGE_INTEGER CmosTime;
  950. ExAcquireSpinLock(&ExpWakeTimerListLock, &OldIrql);
  951. BestDueTime = 0;
  952. BestTimer = NULL;
  953. Link = ExpWakeTimerList.Flink;
  954. while (Link != &ExpWakeTimerList) {
  955. ExTimer = CONTAINING_RECORD(Link, ETIMER, WakeTimerListEntry);
  956. Link = Link->Flink;
  957. if (ExTimer->WakeTimer) {
  958. TimerDueTime = KeQueryTimerDueTime(&ExTimer->KeTimer);
  959. TimerDueTime = 0 - TimerDueTime;
  960. //
  961. // Is this timers due time closer?
  962. //
  963. if (TimerDueTime > BestDueTime) {
  964. BestDueTime = TimerDueTime;
  965. BestTimer = ExTimer;
  966. }
  967. } else {
  968. //
  969. // Timer is not an active wake timer, remove it
  970. //
  971. RemoveEntryList(&ExTimer->WakeTimerListEntry);
  972. ExTimer->WakeTimerListEntry.Flink = NULL;
  973. }
  974. }
  975. ExReleaseSpinLock(&ExpWakeTimerListLock, OldIrql);
  976. if (BestDueTime) {
  977. //
  978. // Convert time to timefields
  979. //
  980. KeQuerySystemTime (&SystemTime);
  981. InterruptTime = KeQueryInterruptTime ();
  982. BestDueTime = 0 - BestDueTime;
  983. SystemTime.QuadPart += BestDueTime - InterruptTime;
  984. //
  985. // Many system alarms are only good to 1 second resolution.
  986. // Add one sceond to the target time so that the timer is really
  987. // elasped if this is the wake event.
  988. //
  989. SystemTime.QuadPart += 10000000;
  990. ExSystemTimeToLocalTime(&SystemTime,&CmosTime);
  991. RtlTimeToTimeFields(&CmosTime, TimeFields);
  992. }
  993. *DueTime = BestDueTime;
  994. *TimerObject = BestTimer;
  995. }