- Support INIT section pragmas for msvc. Patch by Brezenbak.
[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 #if defined (ALLOC_PRAGMA)
24 #pragma alloc_text(INIT, KiInitializeSystemClock)
25 #endif
26
27 /* GLOBALS ****************************************************************/
28
29 /*
30 * Current time
31 */
32 #if defined(__GNUC__)
33 LARGE_INTEGER SystemBootTime = (LARGE_INTEGER)0LL;
34 #else
35 LARGE_INTEGER SystemBootTime = { 0 };
36 #endif
37
38 CHAR KiTimerSystemAuditing = 0;
39 static KDPC KiExpireTimerDpc;
40 static BOOLEAN KiClockSetupComplete = FALSE;
41
42 /*
43 * Number of timer interrupts since initialisation
44 */
45 volatile ULONGLONG KeTickCount = 0;
46 volatile ULONG KiRawTicks = 0;
47
48 extern LIST_ENTRY KiTimerListHead;
49
50 /*
51 * The increment in the system clock every timer tick (in system time units)
52 *
53 * = (1/18.2)*10^9
54 *
55 * RJJ was 54945055
56 */
57 #define CLOCK_INCREMENT (100000)
58
59 ULONG KeMaximumIncrement = 100000;
60 ULONG KeMinimumIncrement = 100000;
61
62 #define MICROSECONDS_PER_TICK (10000)
63 #define TICKS_TO_CALIBRATE (1)
64 #define CALIBRATE_PERIOD (MICROSECONDS_PER_TICK * TICKS_TO_CALIBRATE)
65
66 /* FUNCTIONS **************************************************************/
67
68 /*
69 * FUNCTION: Initializes timer irq handling
70 * NOTE: This is only called once from main()
71 */
72 VOID
73 INIT_FUNCTION
74 NTAPI
75 KiInitializeSystemClock(VOID)
76 {
77 TIME_FIELDS TimeFields;
78
79 DPRINT("KiInitializeSystemClock()\n");
80 InitializeListHead(&KiTimerListHead);
81 KeInitializeDpc(&KiExpireTimerDpc, (PKDEFERRED_ROUTINE)KiExpireTimers, 0);
82
83 /* Calculate the starting time for the system clock */
84 HalQueryRealTimeClock(&TimeFields);
85 RtlTimeFieldsToTime(&TimeFields, &SystemBootTime);
86
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;
96
97 KiClockSetupComplete = TRUE;
98 DPRINT("Finished KiInitializeSystemClock()\n");
99 }
100
101 VOID
102 NTAPI
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->SegCs & MODE_MASK ||
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 * 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
316 * flag is set.
317 */
318 if ((CurrentThread->Quantum -= 3) <= 0)
319 {
320 Prcb->QuantumEnd = TRUE;
321 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
322 }
323 }
324
325
326 /*
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
330 * that prototype.
331 *
332 * @implemented
333 */
334 VOID
335 STDCALL
336 KeUpdateSystemTime(
337 IN PKTRAP_FRAME TrapFrame,
338 IN KIRQL Irql
339 )
340 /*
341 * FUNCTION: Handles a timer interrupt
342 */
343 {
344 LARGE_INTEGER Time;
345
346 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL);
347
348 KiRawTicks++;
349
350 if (KiClockSetupComplete == FALSE) return;
351
352 /*
353 * Increment the number of timers ticks
354 */
355 KeTickCount++;
356 SharedUserData->TickCountLowDeprecated++;
357
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;
364
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;
371
372 /* FIXME: Here we should check for remote debugger break-ins */
373
374 /* Update process and thread times */
375 KeUpdateRunTime(TrapFrame, Irql);
376
377 /*
378 * Queue a DPC that will expire timers
379 */
380 KeInsertQueueDpc(&KiExpireTimerDpc, (PVOID)TrapFrame->Eip, 0);
381 }
382
383 /*
384 * @implemented
385 */
386 ULONG
387 STDCALL
388 NtGetTickCount(VOID)
389 {
390 LARGE_INTEGER TickCount;
391
392 KeQueryTickCount(&TickCount);
393 return TickCount.u.LowPart;
394 }
395
396 NTSTATUS
397 STDCALL
398 NtQueryTimerResolution(OUT PULONG MinimumResolution,
399 OUT PULONG MaximumResolution,
400 OUT PULONG ActualResolution)
401 {
402 UNIMPLEMENTED;
403 return STATUS_NOT_IMPLEMENTED;
404 }
405
406 NTSTATUS
407 STDCALL
408 NtSetTimerResolution(IN ULONG DesiredResolution,
409 IN BOOLEAN SetResolution,
410 OUT PULONG CurrentResolution)
411 {
412 UNIMPLEMENTED;
413 return STATUS_NOT_IMPLEMENTED;
414 }
415
416 /* EOF */