1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ex/time.c
5 * PURPOSE: Time and Timezone Management
6 * PROGRAMMERS: Eric Kohl
7 * Thomas Weidenmueller
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define TICKSPERMINUTE 600000000
17
18 /* GLOBALS ******************************************************************/
19
20 /* Note: Bias[minutes] = UTC - local time */
21 RTL_TIME_ZONE_INFORMATION ExpTimeZoneInfo;
22 ULONG ExpLastTimeZoneBias = -1;
23 LARGE_INTEGER ExpTimeZoneBias;
24 ULONG ExpAltTimeZoneBias;
25 ULONG ExpTimeZoneId;
26 ULONG ExpTickCountMultiplier;
27 ERESOURCE ExpTimeRefreshLock;
28 ULONG ExpKernelResolutionCount = 0;
29 ULONG ExpTimerResolutionCount = 0;
30
31 /* FUNCTIONS ****************************************************************/
32
33 /*++
34 * If successful, this function sets the following global variable:
35 * ExpTimeZoneInfo
36 *--*/
37 static
38 BOOLEAN
ExpGetTimeZoneId(_In_ PLARGE_INTEGER TimeNow,_Out_ PULONG TimeZoneId,_Out_ PLARGE_INTEGER NewTimeZoneBias)39 ExpGetTimeZoneId(
40 _In_ PLARGE_INTEGER TimeNow,
41 _Out_ PULONG TimeZoneId,
42 _Out_ PLARGE_INTEGER NewTimeZoneBias)
43 {
44 LARGE_INTEGER StandardTime;
45 LARGE_INTEGER DaylightTime;
46 LARGE_INTEGER LocalTimeNow = *TimeNow;
47 NTSTATUS Status;
48
49 DPRINT("ExpGetTimeZoneId\n");
50
51 /* Read time zone information from the registry */
52 Status = RtlQueryTimeZoneInformation(&ExpTimeZoneInfo);
53 if (!NT_SUCCESS(Status))
54 {
55 DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
56 return FALSE;
57 }
58
59 /* Get the default bias */
60 NewTimeZoneBias->QuadPart = (LONGLONG)ExpTimeZoneInfo.Bias * TICKSPERMINUTE;
61
62 if (ExpTimeZoneInfo.StandardDate.Month != 0 &&
63 ExpTimeZoneInfo.DaylightDate.Month != 0)
64 {
65 /* Get this year's standard start time */
66 if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.StandardDate,
67 &StandardTime,
68 &LocalTimeNow,
69 TRUE))
70 {
71 DPRINT1("RtlCutoverTimeToSystemTime for StandardDate failed\n");
72 return FALSE;
73 }
74
75 /* Get this year's daylight start time */
76 if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.DaylightDate,
77 &DaylightTime,
78 &LocalTimeNow,
79 TRUE))
80 {
81 DPRINT1("RtlCutoverTimeToSystemTime for DaylightDate failed\n");
82 return FALSE;
83 }
84
85 /* Determine the time zone id and update the time zone bias */
86 if (DaylightTime.QuadPart < StandardTime.QuadPart)
87 {
88 if ((LocalTimeNow.QuadPart >= DaylightTime.QuadPart) &&
89 (LocalTimeNow.QuadPart < StandardTime.QuadPart))
90 {
91 DPRINT("Daylight time\n");
92 *TimeZoneId = TIME_ZONE_ID_DAYLIGHT;
93 NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
94 }
95 else
96 {
97 DPRINT("Standard time\n");
98 *TimeZoneId = TIME_ZONE_ID_STANDARD;
99 NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE;
100 }
101 }
102 else
103 {
104 if ((LocalTimeNow.QuadPart >= StandardTime.QuadPart) &&
105 (LocalTimeNow.QuadPart < DaylightTime.QuadPart))
106 {
107 DPRINT("Standard time\n");
108 *TimeZoneId = TIME_ZONE_ID_STANDARD;
109 NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE;
110 }
111 else
112 {
113 DPRINT("Daylight time\n");
114 *TimeZoneId = TIME_ZONE_ID_DAYLIGHT;
115 NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
116 }
117 }
118 }
119 else
120 {
121 *TimeZoneId = TIME_ZONE_ID_UNKNOWN;
122 }
123
124 return TRUE;
125 }
126
127 /*++
128 * @name ExAcquireTimeRefreshLock
129 *
130 * The ExReleaseTimeRefreshLock routine acquires the system-wide lock used
131 * to synchronize clock interrupt frequency changes.
132 *
133 * @param Wait
134 * If TRUE, the system will block the caller thread waiting for the lock
135 * to become available. If FALSE, the routine will fail if the lock has
136 * already been acquired.
137 *
138 * @return Boolean value indicating success or failure of the lock acquisition.
139 *
140 * @remarks None.
141 *
142 *--*/
143 BOOLEAN
144 NTAPI
ExAcquireTimeRefreshLock(IN BOOLEAN Wait)145 ExAcquireTimeRefreshLock(IN BOOLEAN Wait)
146 {
147 /* Block APCs */
148 KeEnterCriticalRegion();
149
150 /* Attempt lock acquisition */
151 if (!(ExAcquireResourceExclusiveLite(&ExpTimeRefreshLock, Wait)))
152 {
153 /* Lock was not acquired, enable APCs and fail */
154 KeLeaveCriticalRegion();
155 return FALSE;
156 }
157
158 /* Lock has been acquired */
159 return TRUE;
160 }
161
162 /*++
163 * @name ExReleaseTimeRefreshLock
164 *
165 * The ExReleaseTimeRefreshLock routine releases the system-wide lock used
166 * to synchronize clock interrupt frequency changes.
167 *
168 * @param None.
169 *
170 * @return None.
171 *
172 * @remarks None.
173 *
174 *--*/
175 VOID
176 NTAPI
ExReleaseTimeRefreshLock(VOID)177 ExReleaseTimeRefreshLock(VOID)
178 {
179 /* Release the lock and re-enable APCs */
180 ExReleaseResourceLite(&ExpTimeRefreshLock);
181 KeLeaveCriticalRegion();
182 }
183
184 /*++
185 * @name ExSetTimerResolution
186 * @exported
187 *
188 * The KiInsertQueueApc routine modifies the frequency at which the system
189 * clock interrupts.
190 *
191 * @param DesiredTime
192 * Specifies the amount of time that should elapse between each timer
193 * interrupt, in 100-nanosecond units.
194 *
195 * This parameter is ignored if SetResolution is FALSE.
196 *
197 * @param SetResolution
198 * If TRUE, the call is a request to set the clock interrupt frequency to
199 * the value specified by DesiredTime. If FALSE, the call is a request to
200 * restore the clock interrupt frequency to the system's default value.
201 *
202 * @return New timer resolution, in 100-nanosecond ticks.
203 *
204 * @remarks (1) The clock frequency is changed only if the DesiredTime value is
205 * less than the current setting.
206 *
207 * (2) The routine just returns the current setting if the DesiredTime
208 * value is greater than what is currently set.
209 *
210 * (3) If the DesiredTime value is less than the system clock can
211 * support, the routine uses the smallest resolution the system can
212 * support, and returns that value.
213 *
214 * (4) If multiple drivers have attempted to change the clock interrupt
215 * frequency, the system will only restore the default frequency
216 * once ALL drivers have called the routine with SetResolution set
217 * to FALSE.
218 *
219 * NB. This routine synchronizes with IRP_MJ_POWER requests through the
220 * TimeRefreshLock.
221 *
222 *--*/
223 ULONG
224 NTAPI
ExSetTimerResolution(IN ULONG DesiredTime,IN BOOLEAN SetResolution)225 ExSetTimerResolution(IN ULONG DesiredTime,
226 IN BOOLEAN SetResolution)
227 {
228 ULONG CurrentIncrement;
229
230 /* Wait for clock interrupt frequency and power requests to synchronize */
231 ExAcquireTimeRefreshLock(TRUE);
232
233 /* Obey remark 2*/
234 CurrentIncrement = KeTimeIncrement;
235
236 /* Check the type of operation this is */
237 if (SetResolution)
238 {
239 /*
240 * If this is the first kernel change, bump the timer resolution change
241 * count, then bump the kernel change count as well.
242 *
243 * These two variables are tracked differently since user-mode processes
244 * can also change the timer resolution through the NtSetTimerResolution
245 * system call. A per-process flag in the EPROCESS then stores the per-
246 * process change state.
247 *
248 */
249 if (!ExpKernelResolutionCount++) ExpTimerResolutionCount++;
250
251 /* Obey remark 3 */
252 if (DesiredTime < KeMinimumIncrement) DesiredTime = KeMinimumIncrement;
253
254 /* Obey remark 1 */
255 if (DesiredTime < KeTimeIncrement)
256 {
257 /* Force this thread on CPU zero, since we don't want it to drift */
258 KeSetSystemAffinityThread(1);
259
260 /* Now call the platform driver (HAL) to make the change */
261 CurrentIncrement = HalSetTimeIncrement(DesiredTime);
262
263 /* Put the thread back to its original affinity */
264 KeRevertToUserAffinityThread();
265
266 /* Finally, keep track of the new value in the kernel */
267 KeTimeIncrement = CurrentIncrement;
268 }
269 }
270 else
271 {
272 /* First, make sure that a driver has actually changed the resolution */
273 if (ExpKernelResolutionCount)
274 {
275 /* Obey remark 4 */
276 if (--ExpKernelResolutionCount)
277 {
278 /*
279 * All kernel drivers have requested the original frequency to
280 * be restored, but there might still be user processes with an
281 * ongoing clock interrupt frequency change, so make sure that
282 * this isn't the case.
283 */
284 if (--ExpTimerResolutionCount)
285 {
286 /* Force this thread on one CPU so that it doesn't drift */
287 KeSetSystemAffinityThread(1);
288
289 /* Call the HAL to restore the frequency to its default */
290 CurrentIncrement = HalSetTimeIncrement(KeMaximumIncrement);
291
292 /* Put the thread back to its original affinity */
293 KeRevertToUserAffinityThread();
294
295 /* Finally, keep track of the new value in the kernel */
296 KeTimeIncrement = CurrentIncrement;
297 }
298 }
299 }
300 }
301
302 /* Release the clock interrupt frequency lock since changes are done */
303 ExReleaseTimeRefreshLock();
304
305 /* And return the current value -- which could reflect the new frequency */
306 return CurrentIncrement;
307 }
308
309 VOID
310 NTAPI
ExUpdateSystemTimeFromCmos(IN BOOLEAN UpdateInterruptTime,IN ULONG MaxSepInSeconds)311 ExUpdateSystemTimeFromCmos(IN BOOLEAN UpdateInterruptTime,
312 IN ULONG MaxSepInSeconds)
313 {
314 /* FIXME: TODO */
315 return;
316 }
317
318 BOOLEAN
319 NTAPI
ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime)320 ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime)
321 {
322 LARGE_INTEGER CurrentTime, NewTimeZoneBias;
323 BOOLEAN Success;
324
325 DPRINT("ExRefreshTimeZoneInformation\n");
326
327 /* Set the global data for ExpTimeZoneBias and the Time Zone ID */
328 Success = ExpGetTimeZoneId(CurrentBootTime, &ExpTimeZoneId, &NewTimeZoneBias);
329 if (!Success)
330 {
331 DPRINT1("ExpGetTimeZoneId failed\n");
332 return FALSE;
333 }
334 DPRINT("ExpTimeZoneId is %lu\n", ExpTimeZoneId);
335
336 ExpTimeZoneBias = NewTimeZoneBias;
337
338 /* Change SharedUserData->TimeZoneBias for user-mode applications */
339 SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.u.HighPart;
340 SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.u.LowPart;
341 SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.u.HighPart;
342 SharedUserData->TimeZoneId = ExpTimeZoneId;
343
344 /* Convert boot time from local time to UTC */
345 KeBootTime.QuadPart += ExpTimeZoneBias.QuadPart;
346
347 /* Convert system time from local time to UTC */
348 KeQuerySystemTime(&CurrentTime);;
349
350 /* Change it for user-mode applications */
351 CurrentTime.QuadPart += ExpTimeZoneBias.QuadPart;
352 SharedUserData->SystemTime.High2Time = CurrentTime.u.HighPart;
353 SharedUserData->SystemTime.LowPart = CurrentTime.u.LowPart;
354 SharedUserData->SystemTime.High1Time = CurrentTime.u.HighPart;
355
356 /* Return success */
357 return TRUE;
358 }
359
360 NTSTATUS
ExpSetTimeZoneInformation(PRTL_TIME_ZONE_INFORMATION TimeZoneInformation)361 ExpSetTimeZoneInformation(PRTL_TIME_ZONE_INFORMATION TimeZoneInformation)
362 {
363 LARGE_INTEGER LocalTime, SystemTime, OldTime, NewTimeZoneBias;
364 TIME_FIELDS TimeFields;
365 BOOLEAN Success;
366 NTSTATUS Status;
367 LARGE_INTEGER LocalTimeNow;
368
369 DPRINT("ExpSetTimeZoneInformation called\n");
370
371 /* Read time zone information from the registry */
372 Status = RtlQueryTimeZoneInformation(&ExpTimeZoneInfo);
373 if (!NT_SUCCESS(Status))
374 {
375 DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
376 return FALSE;
377 }
378
379 /* Get the default bias */
380 NewTimeZoneBias.QuadPart = (LONGLONG)ExpTimeZoneInfo.Bias * TICKSPERMINUTE;
381
382 /* Get the Shared User Data System Time into the local SystemTime */
383 KeQuerySystemTime(&SystemTime);
384
385 LocalTimeNow = SystemTime;
386
387 /* Adjust LocalTimeNow for Time Zone Bias to use UTC for comparisons */
388 LocalTimeNow.QuadPart -= NewTimeZoneBias.QuadPart;
389
390 /* Set the Global Data for ExpTimeZoneBias and ExpTimeZoneId */
391 Success = ExpGetTimeZoneId(&LocalTimeNow, &ExpTimeZoneId, &NewTimeZoneBias);
392 if (!Success)
393 {
394 DPRINT1("ExpGetTimeZoneId failed\n");
395 return STATUS_UNSUCCESSFUL;
396 }
397 DPRINT("ExpTimeZoneId is %lu\n", ExpTimeZoneId);
398
399 ExpTimeZoneBias = NewTimeZoneBias;
400
401 DPRINT("Old time zone bias: %d minutes\n", ExpTimeZoneInfo.Bias);
402 DPRINT("Old time zone standard bias: %d minutes\n",
403 ExpTimeZoneInfo.StandardBias);
404 DPRINT("New time zone bias: %d minutes\n", TimeZoneInformation->Bias);
405 DPRINT("New time zone standard bias: %d minutes\n",
406 TimeZoneInformation->StandardBias);
407
408 /* Get the local time */
409 HalQueryRealTimeClock(&TimeFields);
410 RtlTimeFieldsToTime(&TimeFields, &LocalTime);
411
412 /* Calculate the bias */
413 ExpTimeZoneBias.QuadPart = ((LONGLONG)(TimeZoneInformation->Bias +
414 TimeZoneInformation->StandardBias)) *
415 TICKSPERMINUTE;
416
417 /* If Daylight Savings Time then add the DayLightBias to the Time Zone Bias */
418 if (ExpTimeZoneId == TIME_ZONE_ID_DAYLIGHT)
419 ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
420
421 /* Copy the timezone information */
422 RtlCopyMemory(&ExpTimeZoneInfo,
423 TimeZoneInformation,
424 sizeof(RTL_TIME_ZONE_INFORMATION));
425
426 /* Set the new time zone information */
427 SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.u.HighPart;
428 SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.u.HighPart;
429 SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.u.LowPart;
430 SharedUserData->TimeZoneId = ExpTimeZoneId;
431
432 DPRINT("New time zone bias: %I64d minutes\n",
433 ExpTimeZoneBias.QuadPart / TICKSPERMINUTE);
434
435 /* Calculate the new system time */
436 ExLocalTimeToSystemTime(&LocalTime, &SystemTime);
437
438 /* Set the new system time and notify the system */
439 KeSetSystemTime(&SystemTime, &OldTime, FALSE, NULL);
440 PoNotifySystemTimeSet();
441
442 /* Return success */
443 DPRINT("ExpSetTimeZoneInformation done\n");
444 return STATUS_SUCCESS;
445 }
446
447 /*
448 * FUNCTION: Sets the system time.
449 * PARAMETERS:
450 * NewTime - Points to a variable that specified the new time
451 * of day in the standard time format.
452 * OldTime - Optionally points to a variable that receives the
453 * old time of day in the standard time format.
454 * RETURNS: Status
455 */
456 NTSTATUS
457 NTAPI
NtSetSystemTime(IN PLARGE_INTEGER SystemTime,OUT PLARGE_INTEGER PreviousTime OPTIONAL)458 NtSetSystemTime(IN PLARGE_INTEGER SystemTime,
459 OUT PLARGE_INTEGER PreviousTime OPTIONAL)
460 {
461 LARGE_INTEGER OldSystemTime;
462 LARGE_INTEGER NewSystemTime;
463 LARGE_INTEGER LocalTime;
464 TIME_FIELDS TimeFields;
465 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
466 NTSTATUS Status = STATUS_SUCCESS;
467 RTL_TIME_ZONE_INFORMATION TimeZoneInformation = { 0 };
468 ULONG TimeZoneIdSave;
469
470 PAGED_CODE();
471
472 // TODO: Handle the case when SystemTime == NULL, which means:
473 // "update system time using the current time-zone information".
474 if (!SystemTime)
475 {
476 UNIMPLEMENTED;
477 return STATUS_NOT_IMPLEMENTED;
478 }
479
480 /* Check if we were called from user-mode */
481 if (PreviousMode != KernelMode)
482 {
483 _SEH2_TRY
484 {
485 /* Verify the time pointers */
486 NewSystemTime = ProbeForReadLargeInteger(SystemTime);
487 if (PreviousTime) ProbeForWriteLargeInteger(PreviousTime);
488 }
489 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
490 {
491 /* Return the exception code */
492 _SEH2_YIELD(return _SEH2_GetExceptionCode());
493 }
494 _SEH2_END;
495 }
496 else
497 {
498 /* Reuse the pointer */
499 NewSystemTime = *SystemTime;
500 }
501
502 /* Make sure we have permission to change the time */
503 if (!SeSinglePrivilegeCheck(SeSystemtimePrivilege, PreviousMode))
504 {
505 DPRINT1("NtSetSystemTime: Caller requires the "
506 "SeSystemtimePrivilege privilege!\n");
507 return STATUS_PRIVILEGE_NOT_HELD;
508 }
509
510 /* Convert the time and set it in HAL */
511 ExSystemTimeToLocalTime(&NewSystemTime, &LocalTime);
512 RtlTimeToTimeFields(&LocalTime, &TimeFields);
513 HalSetRealTimeClock(&TimeFields);
514
515 /* Now set the system time and notify the system */
516 KeSetSystemTime(&NewSystemTime, &OldSystemTime, FALSE, NULL);
517 PoNotifySystemTimeSet();
518
519 /* Check if caller wanted previous time */
520 if (PreviousTime)
521 {
522 /* Enter SEH Block for return */
523 _SEH2_TRY
524 {
525 /* Return the previous time */
526 *PreviousTime = OldSystemTime;
527 }
528 _SEH2_EXCEPT(ExSystemExceptionFilter())
529 {
530 /* Get the exception code */
531 Status = _SEH2_GetExceptionCode();
532 }
533 _SEH2_END;
534 }
535
536 /* Read time zone information from the registry and set the clock */
537 Status = RtlQueryTimeZoneInformation(&TimeZoneInformation);
538 if (!NT_SUCCESS(Status))
539 {
540 DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
541 }
542
543 /* Test if we went from Daylight to Standard Time or vice versa */
544 TimeZoneIdSave = ExpTimeZoneId;
545 ExpSetTimeZoneInformation(&TimeZoneInformation);
546
547 if (ExpTimeZoneId != TimeZoneIdSave)
548 {
549 /* Going from DT to ST or vice versa we need to repeat this */
550 DPRINT("Daylight Time and Standard Time are switching\n");
551
552 /* Set the system time and notify the system */
553 KeSetSystemTime(&NewSystemTime, &OldSystemTime, FALSE, NULL);
554 PoNotifySystemTimeSet();
555 }
556
557 /* Return status */
558 return Status;
559 }
560
561 /*
562 * FUNCTION: Retrieves the system time.
563 * PARAMETERS:
564 * CurrentTime - Points to a variable that receives the current
565 * time of day in the standard time format.
566 */
567 NTSTATUS
568 NTAPI
NtQuerySystemTime(OUT PLARGE_INTEGER SystemTime)569 NtQuerySystemTime(OUT PLARGE_INTEGER SystemTime)
570 {
571 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
572 PAGED_CODE();
573
574 /* Check if we were called from user-mode */
575 if (PreviousMode != KernelMode)
576 {
577 _SEH2_TRY
578 {
579 /* Verify the time pointer */
580 ProbeForWriteLargeInteger(SystemTime);
581
582 /*
583 * It's safe to pass the pointer directly to KeQuerySystemTime
584 * as it's just a basic copy to this pointer. If it raises an
585 * exception nothing dangerous can happen!
586 */
587 KeQuerySystemTime(SystemTime);
588 }
589 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
590 {
591 /* Return the exception code */
592 _SEH2_YIELD(return _SEH2_GetExceptionCode());
593 }
594 _SEH2_END;
595 }
596 else
597 {
598 /* Query the time directly */
599 KeQuerySystemTime(SystemTime);
600 }
601
602 /* Return success */
603 return STATUS_SUCCESS;
604 }
605
606 /*
607 * @implemented
608 */
609 VOID
610 NTAPI
ExLocalTimeToSystemTime(PLARGE_INTEGER LocalTime,PLARGE_INTEGER SystemTime)611 ExLocalTimeToSystemTime(PLARGE_INTEGER LocalTime,
612 PLARGE_INTEGER SystemTime)
613 {
614 SystemTime->QuadPart = LocalTime->QuadPart + ExpTimeZoneBias.QuadPart;
615 }
616
617 /*
618 * @implemented
619 */
620 VOID
621 NTAPI
ExSystemTimeToLocalTime(PLARGE_INTEGER SystemTime,PLARGE_INTEGER LocalTime)622 ExSystemTimeToLocalTime(PLARGE_INTEGER SystemTime,
623 PLARGE_INTEGER LocalTime)
624 {
625 LocalTime->QuadPart = SystemTime->QuadPart - ExpTimeZoneBias.QuadPart;
626 }
627
628 /*
629 * @implemented
630 */
631 NTSTATUS
632 NTAPI
NtQueryTimerResolution(OUT PULONG MinimumResolution,OUT PULONG MaximumResolution,OUT PULONG ActualResolution)633 NtQueryTimerResolution(OUT PULONG MinimumResolution,
634 OUT PULONG MaximumResolution,
635 OUT PULONG ActualResolution)
636 {
637 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
638
639 /* Check if the call came from user-mode */
640 if (PreviousMode != KernelMode)
641 {
642 _SEH2_TRY
643 {
644 /* Probe the parameters */
645 ProbeForWriteUlong(MinimumResolution);
646 ProbeForWriteUlong(MaximumResolution);
647 ProbeForWriteUlong(ActualResolution);
648
649 /*
650 * Set the parameters to the actual values.
651 *
652 * NOTE:
653 * MinimumResolution corresponds to the biggest time increment and
654 * MaximumResolution corresponds to the smallest time increment.
655 */
656 *MinimumResolution = KeMaximumIncrement;
657 *MaximumResolution = KeMinimumIncrement;
658 *ActualResolution = KeTimeIncrement;
659 }
660 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
661 {
662 /* Return the exception code */
663 _SEH2_YIELD(return _SEH2_GetExceptionCode());
664 }
665 _SEH2_END;
666 }
667 else
668 {
669 /* Set the parameters to the actual values */
670 *MinimumResolution = KeMaximumIncrement;
671 *MaximumResolution = KeMinimumIncrement;
672 *ActualResolution = KeTimeIncrement;
673 }
674
675 /* Return success */
676 return STATUS_SUCCESS;
677 }
678
679 /*
680 * @implemented
681 */
682 NTSTATUS
683 NTAPI
NtSetTimerResolution(IN ULONG DesiredResolution,IN BOOLEAN SetResolution,OUT PULONG CurrentResolution)684 NtSetTimerResolution(IN ULONG DesiredResolution,
685 IN BOOLEAN SetResolution,
686 OUT PULONG CurrentResolution)
687 {
688 NTSTATUS Status;
689 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
690 PEPROCESS Process = PsGetCurrentProcess();
691 ULONG NewResolution;
692
693 /* Check if the call came from user-mode */
694 if (PreviousMode != KernelMode)
695 {
696 _SEH2_TRY
697 {
698 /* Probe the parameter */
699 ProbeForWriteUlong(CurrentResolution);
700 }
701 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
702 {
703 /* Return the exception code */
704 _SEH2_YIELD(return _SEH2_GetExceptionCode());
705 }
706 _SEH2_END;
707 }
708
709 /* Set and return the new resolution */
710 NewResolution = ExSetTimerResolution(DesiredResolution, SetResolution);
711
712 if (PreviousMode != KernelMode)
713 {
714 _SEH2_TRY
715 {
716 *CurrentResolution = NewResolution;
717 }
718 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
719 {
720 /* Return the exception code */
721 _SEH2_YIELD(return _SEH2_GetExceptionCode());
722 }
723 _SEH2_END;
724 }
725 else
726 {
727 *CurrentResolution = NewResolution;
728 }
729
730 if (SetResolution || Process->SetTimerResolution)
731 {
732 /* The resolution has been changed now or in an earlier call */
733 Status = STATUS_SUCCESS;
734 }
735 else
736 {
737 /* The resolution hasn't been changed */
738 Status = STATUS_TIMER_RESOLUTION_NOT_SET;
739 }
740
741 /* Update the flag */
742 Process->SetTimerResolution = SetResolution;
743
744 return Status;
745 }
746
747 /* EOF */
748