2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/clock.c
5 * PURPOSE: Handle System Clock
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Created
8 * David Welch & Phillip Susi - Implementation (?)
11 /* NOTES ******************************************************************/
13 * System time units are 100-nanosecond intervals
16 /* INCLUDES ***************************************************************/
21 #include <internal/debug.h>
23 #if defined (ALLOC_PRAGMA)
24 #pragma alloc_text(INIT, KiInitializeSystemClock)
27 /* GLOBALS ****************************************************************/
33 LARGE_INTEGER SystemBootTime
= (LARGE_INTEGER
)0LL;
35 LARGE_INTEGER SystemBootTime
= { 0 };
38 CHAR KiTimerSystemAuditing
= 0;
39 static KDPC KiExpireTimerDpc
;
40 static BOOLEAN KiClockSetupComplete
= FALSE
;
43 * Number of timer interrupts since initialisation
45 volatile ULONGLONG KeTickCount
= 0;
46 volatile ULONG KiRawTicks
= 0;
48 extern LIST_ENTRY KiTimerListHead
;
51 * The increment in the system clock every timer tick (in system time units)
57 #define CLOCK_INCREMENT (100000)
59 ULONG KeMaximumIncrement
= 100000;
60 ULONG KeMinimumIncrement
= 100000;
62 #define MICROSECONDS_PER_TICK (10000)
63 #define TICKS_TO_CALIBRATE (1)
64 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
66 /* FUNCTIONS **************************************************************/
69 * FUNCTION: Initializes timer irq handling
70 * NOTE: This is only called once from main()
75 KiInitializeSystemClock(VOID
)
77 TIME_FIELDS TimeFields
;
79 DPRINT("KiInitializeSystemClock()\n");
80 InitializeListHead(&KiTimerListHead
);
81 KeInitializeDpc(&KiExpireTimerDpc
, (PKDEFERRED_ROUTINE
)KiExpireTimers
, 0);
83 /* Calculate the starting time for the system clock */
84 HalQueryRealTimeClock(&TimeFields
);
85 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
87 /* Set up the Used Shared Data */
88 SharedUserData
->TickCountLowDeprecated
= 0;
89 SharedUserData
->TickCountMultiplier
= 167783691; // 2^24 * 1193182 / 119310
90 SharedUserData
->InterruptTime
.High2Time
= 0;
91 SharedUserData
->InterruptTime
.LowPart
= 0;
92 SharedUserData
->InterruptTime
.High1Time
= 0;
93 SharedUserData
->SystemTime
.High2Time
= SystemBootTime
.u
.HighPart
;
94 SharedUserData
->SystemTime
.LowPart
= SystemBootTime
.u
.LowPart
;
95 SharedUserData
->SystemTime
.High1Time
= SystemBootTime
.u
.HighPart
;
97 KiClockSetupComplete
= TRUE
;
98 DPRINT("Finished KiInitializeSystemClock()\n");
103 KiSetSystemTime(PLARGE_INTEGER NewSystemTime
)
105 LARGE_INTEGER OldSystemTime
;
106 LARGE_INTEGER DeltaTime
;
109 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
111 OldIrql
= KeAcquireDispatcherDatabaseLock();
115 OldSystemTime
.u
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
116 OldSystemTime
.u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
118 while (OldSystemTime
.u
.HighPart
!= SharedUserData
->SystemTime
.High2Time
);
120 /* Set the new system time */
121 SharedUserData
->SystemTime
.LowPart
= NewSystemTime
->u
.LowPart
;
122 SharedUserData
->SystemTime
.High1Time
= NewSystemTime
->u
.HighPart
;
123 SharedUserData
->SystemTime
.High2Time
= NewSystemTime
->u
.HighPart
;
125 /* Calculate the difference between the new and the old time */
126 DeltaTime
.QuadPart
= NewSystemTime
->QuadPart
- OldSystemTime
.QuadPart
;
128 /* Update system boot time */
129 SystemBootTime
.QuadPart
+= DeltaTime
.QuadPart
;
131 /* Update absolute timers */
132 DPRINT1("FIXME: TIMER UPDATE NOT DONE!!!\n");
134 KeReleaseDispatcherDatabaseLock(OldIrql
);
137 * NOTE: Expired timers will be processed at the next clock tick!
146 KeQueryTimeIncrement(VOID
)
148 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
149 * the system clock every time the clock interrupts
150 * RETURNS: The increment
153 return KeMaximumIncrement
;
161 KeQueryTickCount(PLARGE_INTEGER TickCount
)
163 * FUNCTION: Returns the number of ticks since the system was booted
165 * TickCount (OUT) = Points to storage for the number of ticks
168 TickCount
->QuadPart
= KeTickCount
;
172 * FUNCTION: Gets the current system time
174 * CurrentTime (OUT) = The routine stores the current time here
175 * NOTE: The time is the number of 100-nanosecond intervals since the
176 * 1st of January, 1601.
182 KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
185 CurrentTime
->u
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
186 CurrentTime
->u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
187 } while (CurrentTime
->u
.HighPart
!= SharedUserData
->SystemTime
.High2Time
);
192 KeQueryInterruptTime(VOID
)
194 LARGE_INTEGER CurrentTime
;
197 CurrentTime
.u
.HighPart
= SharedUserData
->InterruptTime
.High1Time
;
198 CurrentTime
.u
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
199 } while (CurrentTime
.u
.HighPart
!= SharedUserData
->InterruptTime
.High2Time
);
201 return CurrentTime
.QuadPart
;
210 IN ULONG MaxIncrement
,
211 IN ULONG MinIncrement
)
213 /* Set some Internal Variables */
214 /* FIXME: We use a harcoded CLOCK_INCREMENT. That *must* be changed */
215 KeMaximumIncrement
= MaxIncrement
;
216 KeMinimumIncrement
= MinIncrement
;
224 KeSetTimeUpdateNotifyRoutine(
225 IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine
232 * NOTE: On Windows this function takes exactly one parameter and EBP is
233 * guaranteed to point to KTRAP_FRAME. The function is used only
234 * by HAL, so there's no point in keeping that prototype.
241 IN PKTRAP_FRAME TrapFrame
,
246 PKTHREAD CurrentThread
;
247 PKPROCESS CurrentProcess
;
252 Prcb
= KeGetCurrentPrcb();
254 /* Make sure we don't go further if we're in early boot phase. */
255 if (Prcb
== NULL
|| Prcb
->CurrentThread
== NULL
)
258 DPRINT("KernelTime %u, UserTime %u \n", Prcb
->KernelTime
, Prcb
->UserTime
);
260 CurrentThread
= Prcb
->CurrentThread
;
261 CurrentProcess
= CurrentThread
->ApcState
.Process
;
264 * Cs bit 0 is always set for user mode if we are in protected mode.
265 * V86 mode is counted as user time.
267 if (TrapFrame
->SegCs
& MODE_MASK
||
268 TrapFrame
->EFlags
& X86_EFLAGS_VM
)
270 InterlockedIncrementUL(&CurrentThread
->UserTime
);
271 InterlockedIncrementUL(&CurrentProcess
->UserTime
);
276 if (Irql
> DISPATCH_LEVEL
)
278 Prcb
->InterruptTime
++;
280 else if (Irql
== DISPATCH_LEVEL
)
286 InterlockedIncrementUL(&CurrentThread
->KernelTime
);
287 InterlockedIncrementUL(&CurrentProcess
->KernelTime
);
293 DpcLastCount
= Prcb
->DpcLastCount
;
294 Prcb
->DpcLastCount
= Prcb
->DpcCount
;
295 Prcb
->DpcRequestRate
= ((Prcb
->DpcCount
- DpcLastCount
) +
296 Prcb
->DpcRequestRate
) / 2;
299 if (Prcb
->DpcData
[0].DpcQueueDepth
> 0 &&
300 Prcb
->DpcRoutineActive
== FALSE
&&
301 Prcb
->DpcInterruptRequested
== FALSE
)
303 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
306 /* FIXME: Do DPC rate adjustments */
309 * If we're at end of quantum request software interrupt. The rest
310 * is handled in KiDispatchInterrupt.
312 * NOTE: If one stays at DISPATCH_LEVEL for a long time the DPC routine
313 * which checks for quantum end will not be executed and decrementing
314 * the quantum here can result in overflow. This is not a problem since
315 * we don't care about the quantum value anymore after the QuantumEnd
318 if ((CurrentThread
->Quantum
-= 3) <= 0)
320 Prcb
->QuantumEnd
= TRUE
;
321 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
327 * NOTE: On Windows this function takes exactly zero parameters and EBP is
328 * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL.
329 * The function is used only by HAL, so there's no point in keeping
337 IN PKTRAP_FRAME TrapFrame
,
341 * FUNCTION: Handles a timer interrupt
346 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL
);
350 if (KiClockSetupComplete
== FALSE
) return;
353 * Increment the number of timers ticks
356 SharedUserData
->TickCountLowDeprecated
++;
358 Time
.u
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
359 Time
.u
.HighPart
= SharedUserData
->InterruptTime
.High1Time
;
360 Time
.QuadPart
+= CLOCK_INCREMENT
;
361 SharedUserData
->InterruptTime
.High2Time
= Time
.u
.HighPart
;
362 SharedUserData
->InterruptTime
.LowPart
= Time
.u
.LowPart
;
363 SharedUserData
->InterruptTime
.High1Time
= Time
.u
.HighPart
;
365 Time
.u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
366 Time
.u
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
367 Time
.QuadPart
+= CLOCK_INCREMENT
;
368 SharedUserData
->SystemTime
.High2Time
= Time
.u
.HighPart
;
369 SharedUserData
->SystemTime
.LowPart
= Time
.u
.LowPart
;
370 SharedUserData
->SystemTime
.High1Time
= Time
.u
.HighPart
;
372 /* FIXME: Here we should check for remote debugger break-ins */
374 /* Update process and thread times */
375 KeUpdateRunTime(TrapFrame
, Irql
);
378 * Queue a DPC that will expire timers
380 KeInsertQueueDpc(&KiExpireTimerDpc
, (PVOID
)TrapFrame
->Eip
, 0);
390 LARGE_INTEGER TickCount
;
392 KeQueryTickCount(&TickCount
);
393 return TickCount
.u
.LowPart
;
398 NtQueryTimerResolution(OUT PULONG MinimumResolution
,
399 OUT PULONG MaximumResolution
,
400 OUT PULONG ActualResolution
)
403 return STATUS_NOT_IMPLEMENTED
;
408 NtSetTimerResolution(IN ULONG DesiredResolution
,
409 IN BOOLEAN SetResolution
,
410 OUT PULONG CurrentResolution
)
413 return STATUS_NOT_IMPLEMENTED
;