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
10 /* INCLUDES *****************************************************************/
16 #define TICKSPERMINUTE 600000000
18 /* GLOBALS ******************************************************************/
20 /* Note: Bias[minutes] = UTC - local time */
21 TIME_ZONE_INFORMATION ExpTimeZoneInfo
;
22 ULONG ExpLastTimeZoneBias
= -1;
23 LARGE_INTEGER ExpTimeZoneBias
;
24 ULONG ExpAltTimeZoneBias
;
26 ULONG ExpTickCountMultiplier
;
27 ERESOURCE ExpTimeRefreshLock
;
28 ULONG ExpKernelResolutionCount
= 0;
29 ULONG ExpTimerResolutionCount
= 0;
31 /* FUNCTIONS ****************************************************************/
34 * @name ExAcquireTimeRefreshLock
36 * The ExReleaseTimeRefreshLock routine acquires the system-wide lock used
37 * to synchronize clock interrupt frequency changes.
40 * If TRUE, the system will block the caller thread waiting for the lock
41 * to become available. If FALSE, the routine will fail if the lock has
42 * already been acquired.
44 * @return Boolean value indicating success or failure of the lock acquisition.
51 ExAcquireTimeRefreshLock(
56 KeEnterCriticalRegion();
58 /* Attempt lock acquisition */
59 if (!(ExAcquireResourceExclusiveLite(&ExpTimeRefreshLock
, Wait
)))
61 /* Lock was not acquired, enable APCs and fail */
62 KeLeaveCriticalRegion();
66 /* Lock has been acquired */
71 * @name ExReleaseTimeRefreshLock
73 * The ExReleaseTimeRefreshLock routine releases the system-wide lock used
74 * to synchronize clock interrupt frequency changes.
85 ExReleaseTimeRefreshLock(
89 /* Release the lock and re-enable APCs */
90 ExReleaseResourceLite(&ExpTimeRefreshLock
);
91 KeLeaveCriticalRegion();
95 * @name ExSetTimerResolution
98 * The KiInsertQueueApc routine modifies the frequency at which the system
102 * Specifies the amount of time that should elapse between each timer
103 * interrupt, in 100-nanosecond units.
105 * This parameter is ignored if SetResolution is FALSE.
107 * @param SetResolution
108 * If TRUE, the call is a request to set the clock interrupt frequency to
109 * the value specified by DesiredTime. If FALSE, the call is a request to
110 * restore the clock interrupt frequency to the system's default value.
112 * @return New timer resolution, in 100-nanosecond ticks.
114 * @remarks (1) The clock frequency is changed only if the DesiredTime value is
115 * less than the current setting.
117 * (2) The routine just returns the current setting if the DesiredTime
118 * value is greater than what is currently set.
120 * (3) If the DesiredTime value is less than the system clock can
121 * support, the routine uses the smallest resolution the system can
122 * support, and returns that value.
124 * (4) If multiple drivers have attempted to change the clock interrupt
125 * frequency, the system will only restore the default frequency
126 * once ALL drivers have called the routine with SetResolution set
129 * NB. This routine synchronizes with IRP_MJ_POWER requests through the
135 ExSetTimerResolution(IN ULONG DesiredTime
,
136 IN BOOLEAN SetResolution
)
138 ULONG CurrentIncrement
;
140 /* Wait for clock interrupt frequency and power requests to synchronize */
141 ExAcquireTimeRefreshLock(TRUE
);
144 CurrentIncrement
= KeTimeIncrement
;
146 /* Check the type of operation this is */
150 * If this is the first kernel change, bump the timer resolution change
151 * count, then bump the kernel change count as well.
153 * These two variables are tracked differently since user-mode processes
154 * can also change the timer resolution through the NtSetTimerResolution
155 * system call. A per-process flag in the EPROCESS then stores the per-
156 * process change state.
159 if (!ExpKernelResolutionCount
++) ExpTimerResolutionCount
++;
162 if (DesiredTime
< KeMinimumIncrement
) DesiredTime
= KeMinimumIncrement
;
165 if (DesiredTime
< KeTimeIncrement
)
167 /* Force this thread on CPU zero, since we don't want it to drift */
168 KeSetSystemAffinityThread(1);
170 /* Now call the platform driver (HAL) to make the change */
171 CurrentIncrement
= HalSetTimeIncrement(DesiredTime
);
173 /* Put the thread back to its original affinity */
174 KeRevertToUserAffinityThread();
176 /* Finally, keep track of the new value in the kernel */
177 KeTimeIncrement
= CurrentIncrement
;
182 /* First, make sure that a driver has actually changed the resolution */
183 if (ExpKernelResolutionCount
)
186 if (--ExpKernelResolutionCount
)
189 * All kernel drivers have requested the original frequency to
190 * be restored, but there might still be user processes with an
191 * ongoing clock interrupt frequency change, so make sure that
192 * this isn't the case.
194 if (--ExpTimerResolutionCount
)
196 /* Force this thread on one CPU so that it doesn't drift */
197 KeSetSystemAffinityThread(1);
199 /* Call the HAL to restore the frequency to its default */
200 CurrentIncrement
= HalSetTimeIncrement(KeMaximumIncrement
);
202 /* Put the thread back to its original affinity */
203 KeRevertToUserAffinityThread();
205 /* Finally, keep track of the new value in the kernel */
206 KeTimeIncrement
= CurrentIncrement
;
212 /* Release the clock interrupt frequency lock since changes are done */
213 ExReleaseTimeRefreshLock();
215 /* And return the current value -- which could reflect the new frequency */
216 return CurrentIncrement
;
221 ExUpdateSystemTimeFromCmos(IN BOOLEAN UpdateInterruptTime
,
222 IN ULONG MaxSepInSeconds
)
230 ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime
)
232 LARGE_INTEGER CurrentTime
;
235 /* Read time zone information from the registry */
236 Status
= RtlQueryTimeZoneInformation(&ExpTimeZoneInfo
);
237 if (!NT_SUCCESS(Status
))
239 /* Failed, clear all data */
240 RtlZeroMemory(&ExpTimeZoneInfo
, sizeof(TIME_ZONE_INFORMATION
));
241 ExpTimeZoneBias
.QuadPart
= (LONGLONG
)0;
242 ExpTimeZoneId
= TIME_ZONE_ID_UNKNOWN
;
246 /* FIXME: Calculate transition dates */
248 /* Set bias and ID */
249 ExpTimeZoneBias
.QuadPart
= ((LONGLONG
)(ExpTimeZoneInfo
.Bias
+
250 ExpTimeZoneInfo
.StandardBias
)) *
252 ExpTimeZoneId
= TIME_ZONE_ID_STANDARD
;
255 /* Change it for user-mode applications */
256 SharedUserData
->TimeZoneBias
.High1Time
= ExpTimeZoneBias
.u
.HighPart
;
257 SharedUserData
->TimeZoneBias
.High2Time
= ExpTimeZoneBias
.u
.HighPart
;
258 SharedUserData
->TimeZoneBias
.LowPart
= ExpTimeZoneBias
.u
.LowPart
;
259 SharedUserData
->TimeZoneId
= ExpTimeZoneId
;
261 /* Convert boot time from local time to UTC */
262 KeBootTime
.QuadPart
+= ExpTimeZoneBias
.QuadPart
;
264 /* Convert system time from local time to UTC */
267 CurrentTime
.u
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
268 CurrentTime
.u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
269 } while (CurrentTime
.u
.HighPart
!= SharedUserData
->SystemTime
.High2Time
);
271 /* Change it for user-mode applications */
272 CurrentTime
.QuadPart
+= ExpTimeZoneBias
.QuadPart
;
273 SharedUserData
->SystemTime
.LowPart
= CurrentTime
.u
.LowPart
;
274 SharedUserData
->SystemTime
.High1Time
= CurrentTime
.u
.HighPart
;
275 SharedUserData
->SystemTime
.High2Time
= CurrentTime
.u
.HighPart
;
282 ExpSetTimeZoneInformation(PTIME_ZONE_INFORMATION TimeZoneInformation
)
284 LARGE_INTEGER LocalTime
, SystemTime
, OldTime
;
285 TIME_FIELDS TimeFields
;
286 DPRINT("ExpSetTimeZoneInformation() called\n");
288 DPRINT("Old time zone bias: %d minutes\n", ExpTimeZoneInfo
.Bias
);
289 DPRINT("Old time zone standard bias: %d minutes\n",
290 ExpTimeZoneInfo
.StandardBias
);
291 DPRINT("New time zone bias: %d minutes\n", TimeZoneInformation
->Bias
);
292 DPRINT("New time zone standard bias: %d minutes\n",
293 TimeZoneInformation
->StandardBias
);
295 /* Get the local time */
296 HalQueryRealTimeClock(&TimeFields
);
297 RtlTimeFieldsToTime(&TimeFields
, &LocalTime
);
299 /* FIXME: Calculate transition dates */
301 /* Calculate the bias and set the ID */
302 ExpTimeZoneBias
.QuadPart
= ((LONGLONG
)(TimeZoneInformation
->Bias
+
303 TimeZoneInformation
->StandardBias
)) *
305 ExpTimeZoneId
= TIME_ZONE_ID_STANDARD
;
307 /* Copy the timezone information */
308 RtlCopyMemory(&ExpTimeZoneInfo
,
310 sizeof(TIME_ZONE_INFORMATION
));
312 /* Set the new time zone information */
313 SharedUserData
->TimeZoneBias
.High1Time
= ExpTimeZoneBias
.u
.HighPart
;
314 SharedUserData
->TimeZoneBias
.High2Time
= ExpTimeZoneBias
.u
.HighPart
;
315 SharedUserData
->TimeZoneBias
.LowPart
= ExpTimeZoneBias
.u
.LowPart
;
316 SharedUserData
->TimeZoneId
= ExpTimeZoneId
;
318 DPRINT("New time zone bias: %I64d minutes\n",
319 ExpTimeZoneBias
.QuadPart
/ TICKSPERMINUTE
);
321 /* Calculate the new system time */
322 ExLocalTimeToSystemTime(&LocalTime
, &SystemTime
);
324 /* Set the new system time */
325 KeSetSystemTime(&SystemTime
, &OldTime
, FALSE
, NULL
);
328 DPRINT("ExpSetTimeZoneInformation() done\n");
329 return STATUS_SUCCESS
;
333 * FUNCTION: Sets the system time.
335 * NewTime - Points to a variable that specified the new time
336 * of day in the standard time format.
337 * OldTime - Optionally points to a variable that receives the
338 * old time of day in the standard time format.
343 NtSetSystemTime(IN PLARGE_INTEGER SystemTime
,
344 OUT PLARGE_INTEGER PreviousTime OPTIONAL
)
346 LARGE_INTEGER OldSystemTime
;
347 LARGE_INTEGER NewSystemTime
;
348 LARGE_INTEGER LocalTime
;
349 TIME_FIELDS TimeFields
;
350 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
351 NTSTATUS Status
= STATUS_SUCCESS
;
354 /* Check if we were called from user-mode */
355 if (PreviousMode
!= KernelMode
)
359 /* Verify the time pointers */
360 NewSystemTime
= ProbeForReadLargeInteger(SystemTime
);
361 if(PreviousTime
) ProbeForWriteLargeInteger(PreviousTime
);
363 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
365 /* Return the exception code */
366 _SEH2_YIELD(return _SEH2_GetExceptionCode());
372 /* Reuse the pointer */
373 NewSystemTime
= *SystemTime
;
376 /* Make sure we have permission to change the time */
377 if (!SeSinglePrivilegeCheck(SeSystemtimePrivilege
, PreviousMode
))
379 DPRINT1("NtSetSystemTime: Caller requires the "
380 "SeSystemtimePrivilege privilege!\n");
381 return STATUS_PRIVILEGE_NOT_HELD
;
384 /* Convert the time and set it in HAL */
385 ExSystemTimeToLocalTime(&NewSystemTime
, &LocalTime
);
386 RtlTimeToTimeFields(&LocalTime
, &TimeFields
);
387 HalSetRealTimeClock(&TimeFields
);
389 /* Now set system time */
390 KeSetSystemTime(&NewSystemTime
, &OldSystemTime
, FALSE
, NULL
);
392 /* Check if caller wanted previous time */
395 /* Enter SEH Block for return */
398 /* Return the previous time */
399 *PreviousTime
= OldSystemTime
;
401 _SEH2_EXCEPT(ExSystemExceptionFilter())
403 /* Get the exception code */
404 Status
= _SEH2_GetExceptionCode();
414 * FUNCTION: Retrieves the system time.
416 * CurrentTime - Points to a variable that receives the current
417 * time of day in the standard time format.
421 NtQuerySystemTime(OUT PLARGE_INTEGER SystemTime
)
423 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
424 NTSTATUS Status
= STATUS_SUCCESS
;
427 /* Check if we were called from user-mode */
428 if (PreviousMode
!= KernelMode
)
432 /* Verify the time pointer */
433 ProbeForWriteLargeInteger(SystemTime
);
436 * It's safe to pass the pointer directly to KeQuerySystemTime as
437 * it's just a basic copy to this pointer. If it raises an
438 * exception nothing dangerous can happen!
440 KeQuerySystemTime(SystemTime
);
442 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
444 /* Get the exception code */
445 Status
= _SEH2_GetExceptionCode();
451 /* Query the time directly */
452 KeQuerySystemTime(SystemTime
);
455 /* Return status to caller */
464 ExLocalTimeToSystemTime(PLARGE_INTEGER LocalTime
,
465 PLARGE_INTEGER SystemTime
)
467 SystemTime
->QuadPart
= LocalTime
->QuadPart
+ ExpTimeZoneBias
.QuadPart
;
475 ExSystemTimeToLocalTime(PLARGE_INTEGER SystemTime
,
476 PLARGE_INTEGER LocalTime
)
478 LocalTime
->QuadPart
= SystemTime
->QuadPart
- ExpTimeZoneBias
.QuadPart
;