KD System Rewrite:
[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 #ifdef __GNUC__
56 ULONG EXPORTED KeMaximumIncrement = 100000;
57 ULONG EXPORTED KeMinimumIncrement = 100000;
58 #else
59 /* Microsoft-style declarations */
60 EXPORTED ULONG KeMaximumIncrement = 100000;
61 EXPORTED ULONG KeMinimumIncrement = 100000;
62 #endif
63
64 #define MICROSECONDS_PER_TICK (10000)
65 #define TICKS_TO_CALIBRATE (1)
66 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
67
68 /* FUNCTIONS **************************************************************/
69
70 /*
71 * FUNCTION: Initializes timer irq handling
72 * NOTE: This is only called once from main()
73 */
74 VOID
75 INIT_FUNCTION
76 KiInitializeSystemClock(VOID)
77 {
78 TIME_FIELDS TimeFields;
79
80 DPRINT1("KiInitializeSystemClock()\n");
81 InitializeListHead(&KiTimerListHead);
82 KeInitializeDpc(&KiExpireTimerDpc, (PKDEFERRED_ROUTINE)KiExpireTimers, 0);
83
84 /* Calculate the starting time for the system clock */
85 HalQueryRealTimeClock(&TimeFields);
86 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
87
88 /* Set up the Used Shared Data */
89 SharedUserData->TickCountLowDeprecated = 0;
90 SharedUserData->TickCountMultiplier = 167783691; // 2^24 * 1193182 / 119310
91 SharedUserData->InterruptTime.High2Time = 0;
92 SharedUserData->InterruptTime.LowPart = 0;
93 SharedUserData->InterruptTime.High1Time = 0;
94 SharedUserData->SystemTime.High2Time = SystemBootTime.u.HighPart;
95 SharedUserData->SystemTime.LowPart = SystemBootTime.u.LowPart;
96 SharedUserData->SystemTime.High1Time = SystemBootTime.u.HighPart;
97
98 KiClockSetupComplete = TRUE;
99 DPRINT1("Finished KiInitializeSystemClock()\n");
100 }
101
102 VOID
103 KiSetSystemTime(PLARGE_INTEGER NewSystemTime)
104 {
105 LARGE_INTEGER OldSystemTime;
106 LARGE_INTEGER DeltaTime;
107 KIRQL OldIrql;
108
109 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
110
111 OldIrql = KeAcquireDispatcherDatabaseLock();
112
113 do
114 {
115 OldSystemTime.u.HighPart = SharedUserData->SystemTime.High1Time;
116 OldSystemTime.u.LowPart = SharedUserData->SystemTime.LowPart;
117 }
118 while (OldSystemTime.u.HighPart != SharedUserData->SystemTime.High2Time);
119
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;
124
125 /* Calculate the difference between the new and the old time */
126 DeltaTime.QuadPart = NewSystemTime->QuadPart - OldSystemTime.QuadPart;
127
128 /* Update system boot time */
129 SystemBootTime.QuadPart += DeltaTime.QuadPart;
130
131 /* Update absolute timers */
132 DPRINT1("FIXME: TIMER UPDATE NOT DONE!!!\n");
133
134 KeReleaseDispatcherDatabaseLock(OldIrql);
135
136 /*
137 * NOTE: Expired timers will be processed at the next clock tick!
138 */
139 }
140
141 /*
142 * @implemented
143 */
144 ULONG
145 STDCALL
146 KeQueryTimeIncrement(VOID)
147 /*
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
151 */
152 {
153 return KeMaximumIncrement;
154 }
155
156 /*
157 * @implemented
158 */
159 VOID
160 STDCALL
161 KeQueryTickCount(PLARGE_INTEGER TickCount)
162 /*
163 * FUNCTION: Returns the number of ticks since the system was booted
164 * ARGUMENTS:
165 * TickCount (OUT) = Points to storage for the number of ticks
166 */
167 {
168 TickCount->QuadPart = KeTickCount;
169 }
170
171 /*
172 * FUNCTION: Gets the current system time
173 * ARGUMENTS:
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.
177 *
178 * @implemented
179 */
180 VOID
181 STDCALL
182 KeQuerySystemTime(PLARGE_INTEGER CurrentTime)
183 {
184 do {
185 CurrentTime->u.HighPart = SharedUserData->SystemTime.High1Time;
186 CurrentTime->u.LowPart = SharedUserData->SystemTime.LowPart;
187 } while (CurrentTime->u.HighPart != SharedUserData->SystemTime.High2Time);
188 }
189
190 ULONGLONG
191 STDCALL
192 KeQueryInterruptTime(VOID)
193 {
194 LARGE_INTEGER CurrentTime;
195
196 do {
197 CurrentTime.u.HighPart = SharedUserData->InterruptTime.High1Time;
198 CurrentTime.u.LowPart = SharedUserData->InterruptTime.LowPart;
199 } while (CurrentTime.u.HighPart != SharedUserData->InterruptTime.High2Time);
200
201 return CurrentTime.QuadPart;
202 }
203
204 /*
205 * @implemented
206 */
207 VOID
208 STDCALL
209 KeSetTimeIncrement(
210 IN ULONG MaxIncrement,
211 IN ULONG MinIncrement)
212 {
213 /* Set some Internal Variables */
214 /* FIXME: We use a harcoded CLOCK_INCREMENT. That *must* be changed */
215 KeMaximumIncrement = MaxIncrement;
216 KeMinimumIncrement = MinIncrement;
217 }
218
219 /*
220 * @unimplemented
221 */
222 VOID
223 FASTCALL
224 KeSetTimeUpdateNotifyRoutine(
225 IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine
226 )
227 {
228 UNIMPLEMENTED;
229 }
230
231 /*
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.
235 *
236 * @implemented
237 */
238 VOID
239 STDCALL
240 KeUpdateRunTime(
241 IN PKTRAP_FRAME TrapFrame,
242 IN KIRQL Irql
243 )
244 {
245 PKPRCB Prcb;
246 PKTHREAD CurrentThread;
247 PKPROCESS CurrentProcess;
248 #if 0
249 ULONG DpcLastCount;
250 #endif
251
252 Prcb = KeGetCurrentPrcb();
253
254 /* Make sure we don't go further if we're in early boot phase. */
255 if (Prcb == NULL || Prcb->CurrentThread == NULL)
256 return;
257
258 DPRINT("KernelTime %u, UserTime %u \n", Prcb->KernelTime, Prcb->UserTime);
259
260 CurrentThread = Prcb->CurrentThread;
261 CurrentProcess = CurrentThread->ApcState.Process;
262
263 /*
264 * Cs bit 0 is always set for user mode if we are in protected mode.
265 * V86 mode is counted as user time.
266 */
267 if (TrapFrame->Cs & 0x1 ||
268 TrapFrame->Eflags & X86_EFLAGS_VM)
269 {
270 InterlockedIncrementUL(&CurrentThread->UserTime);
271 InterlockedIncrementUL(&CurrentProcess->UserTime);
272 Prcb->UserTime++;
273 }
274 else
275 {
276 if (Irql > DISPATCH_LEVEL)
277 {
278 Prcb->InterruptTime++;
279 }
280 else if (Irql == DISPATCH_LEVEL)
281 {
282 Prcb->DpcTime++;
283 }
284 else
285 {
286 InterlockedIncrementUL(&CurrentThread->KernelTime);
287 InterlockedIncrementUL(&CurrentProcess->KernelTime);
288 Prcb->KernelTime++;
289 }
290 }
291
292 #if 0
293 DpcLastCount = Prcb->DpcLastCount;
294 Prcb->DpcLastCount = Prcb->DpcCount;
295 Prcb->DpcRequestRate = ((Prcb->DpcCount - DpcLastCount) +
296 Prcb->DpcRequestRate) / 2;
297 #endif
298
299 if (Prcb->DpcData[0].DpcQueueDepth > 0 &&
300 Prcb->DpcRoutineActive == FALSE &&
301 Prcb->DpcInterruptRequested == FALSE)
302 {
303 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
304 }
305
306 /* FIXME: Do DPC rate adjustments */
307
308 /*
309 * If we're at end of quantum request software interrupt. The rest
310 * is handled in KiDispatchInterrupt.
311 */
312 if ((CurrentThread->Quantum -= 3) <= 0)
313 {
314 Prcb->QuantumEnd = TRUE;
315 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
316 }
317 }
318
319
320 /*
321 * NOTE: On Windows this function takes exactly zero parameters and EBP is
322 * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL.
323 * The function is used only by HAL, so there's no point in keeping
324 * that prototype.
325 *
326 * @implemented
327 */
328 VOID
329 STDCALL
330 KeUpdateSystemTime(
331 IN PKTRAP_FRAME TrapFrame,
332 IN KIRQL Irql
333 )
334 /*
335 * FUNCTION: Handles a timer interrupt
336 */
337 {
338 LARGE_INTEGER Time;
339
340 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL);
341
342 KiRawTicks++;
343
344 if (KiClockSetupComplete == FALSE) return;
345
346 /*
347 * Increment the number of timers ticks
348 */
349 KeTickCount++;
350 SharedUserData->TickCountLowDeprecated++;
351
352 Time.u.LowPart = SharedUserData->InterruptTime.LowPart;
353 Time.u.HighPart = SharedUserData->InterruptTime.High1Time;
354 Time.QuadPart += CLOCK_INCREMENT;
355 SharedUserData->InterruptTime.High2Time = Time.u.HighPart;
356 SharedUserData->InterruptTime.LowPart = Time.u.LowPart;
357 SharedUserData->InterruptTime.High1Time = Time.u.HighPart;
358
359 Time.u.LowPart = SharedUserData->SystemTime.LowPart;
360 Time.u.HighPart = SharedUserData->SystemTime.High1Time;
361 Time.QuadPart += CLOCK_INCREMENT;
362 SharedUserData->SystemTime.High2Time = Time.u.HighPart;
363 SharedUserData->SystemTime.LowPart = Time.u.LowPart;
364 SharedUserData->SystemTime.High1Time = Time.u.HighPart;
365
366 /* FIXME: Here we should check for remote debugger break-ins */
367
368 /* Update process and thread times */
369 KeUpdateRunTime(TrapFrame, Irql);
370
371 /*
372 * Queue a DPC that will expire timers
373 */
374 KeInsertQueueDpc(&KiExpireTimerDpc, (PVOID)TrapFrame->Eip, 0);
375 }
376
377 /*
378 * @implemented
379 */
380 ULONG
381 STDCALL
382 NtGetTickCount(VOID)
383 {
384 LARGE_INTEGER TickCount;
385
386 KeQueryTickCount(&TickCount);
387 return TickCount.u.LowPart;
388 }
389
390 NTSTATUS
391 STDCALL
392 NtQueryTimerResolution(OUT PULONG MinimumResolution,
393 OUT PULONG MaximumResolution,
394 OUT PULONG ActualResolution)
395 {
396 UNIMPLEMENTED;
397 return STATUS_NOT_IMPLEMENTED;
398 }
399
400 NTSTATUS
401 STDCALL
402 NtSetTimerResolution(IN ULONG DesiredResolution,
403 IN BOOLEAN SetResolution,
404 OUT PULONG CurrentResolution)
405 {
406 UNIMPLEMENTED;
407 return STATUS_NOT_IMPLEMENTED;
408 }
409
410 /* EOF */