fixed uninitialized variable warning
[reactos.git] / reactos / ntoskrnl / ke / clock.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/clock.c
5 * PURPOSE: Handle System Clock
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Created
8 * David Welch & Phillip Susi - Implementation (?)
9 */
10
11 /* NOTES ******************************************************************/
12 /*
13 * System time units are 100-nanosecond intervals
14 */
15
16 /* INCLUDES ***************************************************************/
17
18 #include <ntoskrnl.h>
19
20 #define NDEBUG
21 #include <internal/debug.h>
22
23 /* GLOBALS ****************************************************************/
24
25 /*
26 * Current time
27 */
28 #if defined(__GNUC__)
29 LARGE_INTEGER SystemBootTime = (LARGE_INTEGER)0LL;
30 #else
31 LARGE_INTEGER SystemBootTime = { 0 };
32 #endif
33
34 CHAR KiTimerSystemAuditing = 0;
35 static KDPC KiExpireTimerDpc;
36 static BOOLEAN KiClockSetupComplete = FALSE;
37
38 /*
39 * Number of timer interrupts since initialisation
40 */
41 volatile ULONGLONG KeTickCount = 0;
42 volatile ULONG KiRawTicks = 0;
43
44 extern LIST_ENTRY KiTimerListHead;
45
46 /*
47 * The increment in the system clock every timer tick (in system time units)
48 *
49 * = (1/18.2)*10^9
50 *
51 * RJJ was 54945055
52 */
53 #define CLOCK_INCREMENT (100000)
54
55 ULONG KeMaximumIncrement = 100000;
56 ULONG KeMinimumIncrement = 100000;
57
58 #define MICROSECONDS_PER_TICK (10000)
59 #define TICKS_TO_CALIBRATE (1)
60 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
61
62 /* FUNCTIONS **************************************************************/
63
64 /*
65 * FUNCTION: Initializes timer irq handling
66 * NOTE: This is only called once from main()
67 */
68 VOID
69 INIT_FUNCTION
70 NTAPI
71 KiInitializeSystemClock(VOID)
72 {
73 TIME_FIELDS TimeFields;
74
75 DPRINT("KiInitializeSystemClock()\n");
76 InitializeListHead(&KiTimerListHead);
77 KeInitializeDpc(&KiExpireTimerDpc, (PKDEFERRED_ROUTINE)KiExpireTimers, 0);
78
79 /* Calculate the starting time for the system clock */
80 HalQueryRealTimeClock(&TimeFields);
81 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
82
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;
92
93 KiClockSetupComplete = TRUE;
94 DPRINT("Finished KiInitializeSystemClock()\n");
95 }
96
97 VOID
98 NTAPI
99 KiSetSystemTime(PLARGE_INTEGER NewSystemTime)
100 {
101 LARGE_INTEGER OldSystemTime;
102 LARGE_INTEGER DeltaTime;
103 KIRQL OldIrql;
104
105 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
106
107 OldIrql = KeAcquireDispatcherDatabaseLock();
108
109 do
110 {
111 OldSystemTime.u.HighPart = SharedUserData->SystemTime.High1Time;
112 OldSystemTime.u.LowPart = SharedUserData->SystemTime.LowPart;
113 }
114 while (OldSystemTime.u.HighPart != SharedUserData->SystemTime.High2Time);
115
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;
120
121 /* Calculate the difference between the new and the old time */
122 DeltaTime.QuadPart = NewSystemTime->QuadPart - OldSystemTime.QuadPart;
123
124 /* Update system boot time */
125 SystemBootTime.QuadPart += DeltaTime.QuadPart;
126
127 /* Update absolute timers */
128 DPRINT1("FIXME: TIMER UPDATE NOT DONE!!!\n");
129
130 KeReleaseDispatcherDatabaseLock(OldIrql);
131
132 /*
133 * NOTE: Expired timers will be processed at the next clock tick!
134 */
135 }
136
137 /*
138 * @implemented
139 */
140 ULONG
141 STDCALL
142 KeQueryTimeIncrement(VOID)
143 /*
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
147 */
148 {
149 return KeMaximumIncrement;
150 }
151
152 /*
153 * @implemented
154 */
155 VOID
156 STDCALL
157 KeQueryTickCount(PLARGE_INTEGER TickCount)
158 /*
159 * FUNCTION: Returns the number of ticks since the system was booted
160 * ARGUMENTS:
161 * TickCount (OUT) = Points to storage for the number of ticks
162 */
163 {
164 TickCount->QuadPart = KeTickCount;
165 }
166
167 /*
168 * FUNCTION: Gets the current system time
169 * ARGUMENTS:
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.
173 *
174 * @implemented
175 */
176 VOID
177 STDCALL
178 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
179 {
180 do {
181 CurrentTime->u.HighPart = SharedUserData->SystemTime.High1Time;
182 CurrentTime->u.LowPart = SharedUserData->SystemTime.LowPart;
183 } while (CurrentTime->u.HighPart != SharedUserData->SystemTime.High2Time);
184 }
185
186 ULONGLONG
187 STDCALL
188 KeQueryInterruptTime(VOID)
189 {
190 LARGE_INTEGER CurrentTime;
191
192 do {
193 CurrentTime.u.HighPart = SharedUserData->InterruptTime.High1Time;
194 CurrentTime.u.LowPart = SharedUserData->InterruptTime.LowPart;
195 } while (CurrentTime.u.HighPart != SharedUserData->InterruptTime.High2Time);
196
197 return CurrentTime.QuadPart;
198 }
199
200 /*
201 * @implemented
202 */
203 VOID
204 STDCALL
205 KeSetTimeIncrement(
206 IN ULONG MaxIncrement,
207 IN ULONG MinIncrement)
208 {
209 /* Set some Internal Variables */
210 /* FIXME: We use a harcoded CLOCK_INCREMENT. That *must* be changed */
211 KeMaximumIncrement = MaxIncrement;
212 KeMinimumIncrement = MinIncrement;
213 }
214
215 /*
216 * @unimplemented
217 */
218 VOID
219 FASTCALL
220 KeSetTimeUpdateNotifyRoutine(
221 IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine
222 )
223 {
224 UNIMPLEMENTED;
225 }
226
227 /*
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.
231 *
232 * @implemented
233 */
234 VOID
235 STDCALL
236 KeUpdateRunTime(
237 IN PKTRAP_FRAME TrapFrame,
238 IN KIRQL Irql
239 )
240 {
241 PKPRCB Prcb;
242 PKTHREAD CurrentThread;
243 PKPROCESS CurrentProcess;
244 #if 0
245 ULONG DpcLastCount;
246 #endif
247
248 Prcb = KeGetCurrentPrcb();
249
250 /* Make sure we don't go further if we're in early boot phase. */
251 if (Prcb == NULL || Prcb->CurrentThread == NULL)
252 return;
253
254 DPRINT("KernelTime %u, UserTime %u \n", Prcb->KernelTime, Prcb->UserTime);
255
256 CurrentThread = Prcb->CurrentThread;
257 CurrentProcess = CurrentThread->ApcState.Process;
258
259 /*
260 * Cs bit 0 is always set for user mode if we are in protected mode.
261 * V86 mode is counted as user time.
262 */
263 if (TrapFrame->Cs & 0x1 ||
264 TrapFrame->Eflags & X86_EFLAGS_VM)
265 {
266 InterlockedIncrementUL(&CurrentThread->UserTime);
267 InterlockedIncrementUL(&CurrentProcess->UserTime);
268 Prcb->UserTime++;
269 }
270 else
271 {
272 if (Irql > DISPATCH_LEVEL)
273 {
274 Prcb->InterruptTime++;
275 }
276 else if (Irql == DISPATCH_LEVEL)
277 {
278 Prcb->DpcTime++;
279 }
280 else
281 {
282 InterlockedIncrementUL(&CurrentThread->KernelTime);
283 InterlockedIncrementUL(&CurrentProcess->KernelTime);
284 Prcb->KernelTime++;
285 }
286 }
287
288 #if 0
289 DpcLastCount = Prcb->DpcLastCount;
290 Prcb->DpcLastCount = Prcb->DpcCount;
291 Prcb->DpcRequestRate = ((Prcb->DpcCount - DpcLastCount) +
292 Prcb->DpcRequestRate) / 2;
293 #endif
294
295 if (Prcb->DpcData[0].DpcQueueDepth > 0 &&
296 Prcb->DpcRoutineActive == FALSE &&
297 Prcb->DpcInterruptRequested == FALSE)
298 {
299 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
300 }
301
302 /* FIXME: Do DPC rate adjustments */
303
304 /*
305 * If we're at end of quantum request software interrupt. The rest
306 * is handled in KiDispatchInterrupt.
307 *
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
312 * flag is set.
313 */
314 if ((CurrentThread->Quantum -= 3) <= 0)
315 {
316 Prcb->QuantumEnd = TRUE;
317 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
318 }
319 }
320
321
322 /*
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
326 * that prototype.
327 *
328 * @implemented
329 */
330 VOID
331 STDCALL
332 KeUpdateSystemTime(
333 IN PKTRAP_FRAME TrapFrame,
334 IN KIRQL Irql
335 )
336 /*
337 * FUNCTION: Handles a timer interrupt
338 */
339 {
340 LARGE_INTEGER Time;
341
342 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL);
343
344 KiRawTicks++;
345
346 if (KiClockSetupComplete == FALSE) return;
347
348 /*
349 * Increment the number of timers ticks
350 */
351 KeTickCount++;
352 SharedUserData->TickCountLowDeprecated++;
353
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;
360
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;
367
368 /* FIXME: Here we should check for remote debugger break-ins */
369
370 /* Update process and thread times */
371 KeUpdateRunTime(TrapFrame, Irql);
372
373 /*
374 * Queue a DPC that will expire timers
375 */
376 KeInsertQueueDpc(&KiExpireTimerDpc, (PVOID)TrapFrame->Eip, 0);
377 }
378
379 /*
380 * @implemented
381 */
382 ULONG
383 STDCALL
384 NtGetTickCount(VOID)
385 {
386 LARGE_INTEGER TickCount;
387
388 KeQueryTickCount(&TickCount);
389 return TickCount.u.LowPart;
390 }
391
392 NTSTATUS
393 STDCALL
394 NtQueryTimerResolution(OUT PULONG MinimumResolution,
395 OUT PULONG MaximumResolution,
396 OUT PULONG ActualResolution)
397 {
398 UNIMPLEMENTED;
399 return STATUS_NOT_IMPLEMENTED;
400 }
401
402 NTSTATUS
403 STDCALL
404 NtSetTimerResolution(IN ULONG DesiredResolution,
405 IN BOOLEAN SetResolution,
406 OUT PULONG CurrentResolution)
407 {
408 UNIMPLEMENTED;
409 return STATUS_NOT_IMPLEMENTED;
410 }
411
412 /* EOF */