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 /* GLOBALS ****************************************************************/
29 LARGE_INTEGER SystemBootTime
= (LARGE_INTEGER
)0LL;
31 LARGE_INTEGER SystemBootTime
= { 0 };
34 CHAR KiTimerSystemAuditing
= 0;
35 static KDPC KiExpireTimerDpc
;
36 static BOOLEAN KiClockSetupComplete
= FALSE
;
39 * Number of timer interrupts since initialisation
41 volatile ULONGLONG KeTickCount
= 0;
42 volatile ULONG KiRawTicks
= 0;
44 extern LIST_ENTRY KiTimerListHead
;
47 * The increment in the system clock every timer tick (in system time units)
53 #define CLOCK_INCREMENT (100000)
55 ULONG KeMaximumIncrement
= 100000;
56 ULONG KeMinimumIncrement
= 100000;
58 #define MICROSECONDS_PER_TICK (10000)
59 #define TICKS_TO_CALIBRATE (1)
60 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
62 /* FUNCTIONS **************************************************************/
65 * FUNCTION: Initializes timer irq handling
66 * NOTE: This is only called once from main()
71 KiInitializeSystemClock(VOID
)
73 TIME_FIELDS TimeFields
;
75 DPRINT("KiInitializeSystemClock()\n");
76 InitializeListHead(&KiTimerListHead
);
77 KeInitializeDpc(&KiExpireTimerDpc
, (PKDEFERRED_ROUTINE
)KiExpireTimers
, 0);
79 /* Calculate the starting time for the system clock */
80 HalQueryRealTimeClock(&TimeFields
);
81 RtlTimeFieldsToTime(&TimeFields
, &SystemBootTime
);
83 /* Set up the Used Shared Data */
84 SharedUserData
->TickCountLowDeprecated
= 0;
85 SharedUserData
->TickCountMultiplier
= 167783691; // 2^24 * 1193182 / 119310
86 SharedUserData
->InterruptTime
.High2Time
= 0;
87 SharedUserData
->InterruptTime
.LowPart
= 0;
88 SharedUserData
->InterruptTime
.High1Time
= 0;
89 SharedUserData
->SystemTime
.High2Time
= SystemBootTime
.u
.HighPart
;
90 SharedUserData
->SystemTime
.LowPart
= SystemBootTime
.u
.LowPart
;
91 SharedUserData
->SystemTime
.High1Time
= SystemBootTime
.u
.HighPart
;
93 KiClockSetupComplete
= TRUE
;
94 DPRINT("Finished KiInitializeSystemClock()\n");
99 KiSetSystemTime(PLARGE_INTEGER NewSystemTime
)
101 LARGE_INTEGER OldSystemTime
;
102 LARGE_INTEGER DeltaTime
;
105 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
107 OldIrql
= KeAcquireDispatcherDatabaseLock();
111 OldSystemTime
.u
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
112 OldSystemTime
.u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
114 while (OldSystemTime
.u
.HighPart
!= SharedUserData
->SystemTime
.High2Time
);
116 /* Set the new system time */
117 SharedUserData
->SystemTime
.LowPart
= NewSystemTime
->u
.LowPart
;
118 SharedUserData
->SystemTime
.High1Time
= NewSystemTime
->u
.HighPart
;
119 SharedUserData
->SystemTime
.High2Time
= NewSystemTime
->u
.HighPart
;
121 /* Calculate the difference between the new and the old time */
122 DeltaTime
.QuadPart
= NewSystemTime
->QuadPart
- OldSystemTime
.QuadPart
;
124 /* Update system boot time */
125 SystemBootTime
.QuadPart
+= DeltaTime
.QuadPart
;
127 /* Update absolute timers */
128 DPRINT1("FIXME: TIMER UPDATE NOT DONE!!!\n");
130 KeReleaseDispatcherDatabaseLock(OldIrql
);
133 * NOTE: Expired timers will be processed at the next clock tick!
142 KeQueryTimeIncrement(VOID
)
144 * FUNCTION: Gets the increment (in 100-nanosecond units) that is added to
145 * the system clock every time the clock interrupts
146 * RETURNS: The increment
149 return KeMaximumIncrement
;
157 KeQueryTickCount(PLARGE_INTEGER TickCount
)
159 * FUNCTION: Returns the number of ticks since the system was booted
161 * TickCount (OUT) = Points to storage for the number of ticks
164 TickCount
->QuadPart
= KeTickCount
;
168 * FUNCTION: Gets the current system time
170 * CurrentTime (OUT) = The routine stores the current time here
171 * NOTE: The time is the number of 100-nanosecond intervals since the
172 * 1st of January, 1601.
178 KeQuerySystemTime(PLARGE_INTEGER CurrentTime
)
181 CurrentTime
->u
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
182 CurrentTime
->u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
183 } while (CurrentTime
->u
.HighPart
!= SharedUserData
->SystemTime
.High2Time
);
188 KeQueryInterruptTime(VOID
)
190 LARGE_INTEGER CurrentTime
;
193 CurrentTime
.u
.HighPart
= SharedUserData
->InterruptTime
.High1Time
;
194 CurrentTime
.u
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
195 } while (CurrentTime
.u
.HighPart
!= SharedUserData
->InterruptTime
.High2Time
);
197 return CurrentTime
.QuadPart
;
206 IN ULONG MaxIncrement
,
207 IN ULONG MinIncrement
)
209 /* Set some Internal Variables */
210 /* FIXME: We use a harcoded CLOCK_INCREMENT. That *must* be changed */
211 KeMaximumIncrement
= MaxIncrement
;
212 KeMinimumIncrement
= MinIncrement
;
220 KeSetTimeUpdateNotifyRoutine(
221 IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine
228 * NOTE: On Windows this function takes exactly one parameter and EBP is
229 * guaranteed to point to KTRAP_FRAME. The function is used only
230 * by HAL, so there's no point in keeping that prototype.
237 IN PKTRAP_FRAME TrapFrame
,
242 PKTHREAD CurrentThread
;
243 PKPROCESS CurrentProcess
;
248 Prcb
= KeGetCurrentPrcb();
250 /* Make sure we don't go further if we're in early boot phase. */
251 if (Prcb
== NULL
|| Prcb
->CurrentThread
== NULL
)
254 DPRINT("KernelTime %u, UserTime %u \n", Prcb
->KernelTime
, Prcb
->UserTime
);
256 CurrentThread
= Prcb
->CurrentThread
;
257 CurrentProcess
= CurrentThread
->ApcState
.Process
;
260 * Cs bit 0 is always set for user mode if we are in protected mode.
261 * V86 mode is counted as user time.
263 if (TrapFrame
->Cs
& 0x1 ||
264 TrapFrame
->Eflags
& X86_EFLAGS_VM
)
266 InterlockedIncrementUL(&CurrentThread
->UserTime
);
267 InterlockedIncrementUL(&CurrentProcess
->UserTime
);
272 if (Irql
> DISPATCH_LEVEL
)
274 Prcb
->InterruptTime
++;
276 else if (Irql
== DISPATCH_LEVEL
)
282 InterlockedIncrementUL(&CurrentThread
->KernelTime
);
283 InterlockedIncrementUL(&CurrentProcess
->KernelTime
);
289 DpcLastCount
= Prcb
->DpcLastCount
;
290 Prcb
->DpcLastCount
= Prcb
->DpcCount
;
291 Prcb
->DpcRequestRate
= ((Prcb
->DpcCount
- DpcLastCount
) +
292 Prcb
->DpcRequestRate
) / 2;
295 if (Prcb
->DpcData
[0].DpcQueueDepth
> 0 &&
296 Prcb
->DpcRoutineActive
== FALSE
&&
297 Prcb
->DpcInterruptRequested
== FALSE
)
299 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
302 /* FIXME: Do DPC rate adjustments */
305 * If we're at end of quantum request software interrupt. The rest
306 * is handled in KiDispatchInterrupt.
308 * NOTE: If one stays at DISPATCH_LEVEL for a long time the DPC routine
309 * which checks for quantum end will not be executed and decrementing
310 * the quantum here can result in overflow. This is not a problem since
311 * we don't care about the quantum value anymore after the QuantumEnd
314 if ((CurrentThread
->Quantum
-= 3) <= 0)
316 Prcb
->QuantumEnd
= TRUE
;
317 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
323 * NOTE: On Windows this function takes exactly zero parameters and EBP is
324 * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL.
325 * The function is used only by HAL, so there's no point in keeping
333 IN PKTRAP_FRAME TrapFrame
,
337 * FUNCTION: Handles a timer interrupt
342 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL
);
346 if (KiClockSetupComplete
== FALSE
) return;
349 * Increment the number of timers ticks
352 SharedUserData
->TickCountLowDeprecated
++;
354 Time
.u
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
355 Time
.u
.HighPart
= SharedUserData
->InterruptTime
.High1Time
;
356 Time
.QuadPart
+= CLOCK_INCREMENT
;
357 SharedUserData
->InterruptTime
.High2Time
= Time
.u
.HighPart
;
358 SharedUserData
->InterruptTime
.LowPart
= Time
.u
.LowPart
;
359 SharedUserData
->InterruptTime
.High1Time
= Time
.u
.HighPart
;
361 Time
.u
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
362 Time
.u
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
363 Time
.QuadPart
+= CLOCK_INCREMENT
;
364 SharedUserData
->SystemTime
.High2Time
= Time
.u
.HighPart
;
365 SharedUserData
->SystemTime
.LowPart
= Time
.u
.LowPart
;
366 SharedUserData
->SystemTime
.High1Time
= Time
.u
.HighPart
;
368 /* FIXME: Here we should check for remote debugger break-ins */
370 /* Update process and thread times */
371 KeUpdateRunTime(TrapFrame
, Irql
);
374 * Queue a DPC that will expire timers
376 KeInsertQueueDpc(&KiExpireTimerDpc
, (PVOID
)TrapFrame
->Eip
, 0);
386 LARGE_INTEGER TickCount
;
388 KeQueryTickCount(&TickCount
);
389 return TickCount
.u
.LowPart
;
394 NtQueryTimerResolution(OUT PULONG MinimumResolution
,
395 OUT PULONG MaximumResolution
,
396 OUT PULONG ActualResolution
)
399 return STATUS_NOT_IMPLEMENTED
;
404 NtSetTimerResolution(IN ULONG DesiredResolution
,
405 IN BOOLEAN SetResolution
,
406 OUT PULONG CurrentResolution
)
409 return STATUS_NOT_IMPLEMENTED
;