xref: /reactos/ntoskrnl/ex/time.c (revision 0c2cdcae)
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
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
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
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
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
311 ExUpdateSystemTimeFromCmos(IN BOOLEAN UpdateInterruptTime,
312                            IN ULONG MaxSepInSeconds)
313 {
314     /* FIXME: TODO */
315     return;
316 }
317 
318 BOOLEAN
319 NTAPI
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
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
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
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
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
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
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
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