Sync to trunk head (35333)
[reactos.git] / reactos / ntoskrnl / ke / clock.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/clock.c
5 * PURPOSE: System Clock Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 LARGE_INTEGER KeBootTime;
18 ULONGLONG KeBootTimeBias;
19 volatile KSYSTEM_TIME KeTickCount = {0};
20 ULONG KeMaximumIncrement;
21 ULONG KeMinimumIncrement;
22 ULONG KeTimeAdjustment;
23 ULONG KeTimeIncrement;
24 LONG KiTickOffset = 0;
25
26 /* PRIVATE FUNCTIONS *********************************************************/
27
28 VOID
29 NTAPI
30 KeSetSystemTime(IN PLARGE_INTEGER NewTime,
31 OUT PLARGE_INTEGER OldTime,
32 IN BOOLEAN FixInterruptTime,
33 IN PLARGE_INTEGER HalTime OPTIONAL)
34 {
35 TIME_FIELDS TimeFields;
36 KIRQL OldIrql, OldIrql2;
37 LARGE_INTEGER DeltaTime;
38 PLIST_ENTRY ListHead, NextEntry;
39 PKTIMER Timer;
40 PKSPIN_LOCK_QUEUE LockQueue;
41 LIST_ENTRY TempList, TempList2;
42 ULONG Hand, i;
43 PKTIMER_TABLE_ENTRY TimerEntry;
44
45 /* Sanity checks */
46 ASSERT((NewTime->HighPart & 0xF0000000) == 0);
47 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
48
49 /* Check if this is for the HAL */
50 if (HalTime) RtlTimeToTimeFields(HalTime, &TimeFields);
51
52 /* Set affinity to this CPU, lock the dispatcher, and raise IRQL */
53 KeSetSystemAffinityThread(1);
54 OldIrql = KiAcquireDispatcherLock();
55 KeRaiseIrql(HIGH_LEVEL, &OldIrql2);
56
57 /* Query the system time now */
58 KeQuerySystemTime(OldTime);
59
60 /* Set the new system time */
61 SharedUserData->SystemTime.LowPart = NewTime->LowPart;
62 SharedUserData->SystemTime.High1Time = NewTime->HighPart;
63 SharedUserData->SystemTime.High2Time = NewTime->HighPart;
64
65 /* Check if this was for the HAL and set the RTC time */
66 if (HalTime) ExCmosClockIsSane = HalSetRealTimeClock(&TimeFields);
67
68 /* Calculate the difference between the new and the old time */
69 DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart;
70
71 /* Update system boot time */
72 KeBootTime.QuadPart += DeltaTime.QuadPart;
73 KeBootTimeBias = KeBootTimeBias + DeltaTime.QuadPart;
74
75 /* Lower IRQL back */
76 KeLowerIrql(OldIrql2);
77
78 /* Check if we need to adjust interrupt time */
79 if (FixInterruptTime) KEBUGCHECK(0);
80
81 /* Setup a temporary list of absolute timers */
82 InitializeListHead(&TempList);
83
84 /* Loop current timers */
85 for (i = 0; i < TIMER_TABLE_SIZE; i++)
86 {
87 /* Loop the entries in this table and lock the timers */
88 ListHead = &KiTimerTableListHead[i].Entry;
89 LockQueue = KiAcquireTimerLock(i);
90 NextEntry = ListHead->Flink;
91 while (NextEntry != ListHead)
92 {
93 /* Get the timer */
94 Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
95 NextEntry = NextEntry->Flink;
96
97 /* Is it absolute? */
98 if (Timer->Header.Absolute)
99 {
100 /* Remove it from the timer list */
101 if (RemoveEntryList(&Timer->TimerListEntry))
102 {
103 /* Get the entry and check if it's empty */
104 TimerEntry = &KiTimerTableListHead[Timer->Header.Hand];
105 if (IsListEmpty(&TimerEntry->Entry))
106 {
107 /* Clear the time then */
108 TimerEntry->Time.HighPart = 0xFFFFFFFF;
109 }
110 }
111
112 /* Insert it into our temporary list */
113 InsertTailList(&TempList, &Timer->TimerListEntry);
114 }
115 }
116
117 /* Release the lock */
118 KiReleaseTimerLock(LockQueue);
119 }
120
121 /* Setup a temporary list of expired timers */
122 InitializeListHead(&TempList2);
123
124 /* Loop absolute timers */
125 while (TempList.Flink != &TempList)
126 {
127 /* Get the timer */
128 Timer = CONTAINING_RECORD(TempList.Flink, KTIMER, TimerListEntry);
129 RemoveEntryList(&Timer->TimerListEntry);
130
131 /* Update the due time and handle */
132 Timer->DueTime.QuadPart -= DeltaTime.QuadPart;
133 Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart);
134 Timer->Header.Hand = (UCHAR)Hand;
135
136 /* Lock the timer and re-insert it */
137 LockQueue = KiAcquireTimerLock(Hand);
138 if (KiInsertTimerTable(Timer, Hand))
139 {
140 /* Remove it from the timer list */
141 if (RemoveEntryList(&Timer->TimerListEntry))
142 {
143 /* Get the entry and check if it's empty */
144 TimerEntry = &KiTimerTableListHead[Timer->Header.Hand];
145 if (IsListEmpty(&TimerEntry->Entry))
146 {
147 /* Clear the time then */
148 TimerEntry->Time.HighPart = 0xFFFFFFFF;
149 }
150 }
151
152 /* Insert it into our temporary list */
153 InsertTailList(&TempList2, &Timer->TimerListEntry);
154 }
155
156 /* Release the lock */
157 KiReleaseTimerLock(LockQueue);
158 }
159
160 /* FIXME: Process expired timers! */
161 KiReleaseDispatcherLock(OldIrql);
162
163 /* Revert affinity */
164 KeRevertToUserAffinityThread();
165 }
166
167 /* PUBLIC FUNCTIONS **********************************************************/
168
169 /*
170 * @implemented
171 */
172 ULONG
173 NTAPI
174 KeQueryTimeIncrement(VOID)
175 {
176 /* Return the increment */
177 return KeMaximumIncrement;
178 }
179
180 /*
181 * @implemented
182 */
183 #undef KeQueryTickCount
184 VOID
185 NTAPI
186 KeQueryTickCount(IN PLARGE_INTEGER TickCount)
187 {
188 /* Loop until we get a perfect match */
189 for (;;)
190 {
191 /* Read the tick count value */
192 TickCount->HighPart = KeTickCount.High1Time;
193 TickCount->LowPart = KeTickCount.LowPart;
194 if (TickCount->HighPart == KeTickCount.High2Time) break;
195 YieldProcessor();
196 }
197 }
198
199 #ifndef _M_AMD64
200 /*
201 * @implemented
202 */
203 VOID
204 NTAPI
205 KeQuerySystemTime(OUT PLARGE_INTEGER CurrentTime)
206 {
207 /* Loop until we get a perfect match */
208 for (;;)
209 {
210 /* Read the time value */
211 CurrentTime->HighPart = SharedUserData->SystemTime.High1Time;
212 CurrentTime->LowPart = SharedUserData->SystemTime.LowPart;
213 if (CurrentTime->HighPart ==
214 SharedUserData->SystemTime.High2Time) break;
215 YieldProcessor();
216 }
217 }
218
219 /*
220 * @implemented
221 */
222 ULONGLONG
223 NTAPI
224 KeQueryInterruptTime(VOID)
225 {
226 LARGE_INTEGER CurrentTime;
227
228 /* Loop until we get a perfect match */
229 for (;;)
230 {
231 /* Read the time value */
232 CurrentTime.HighPart = SharedUserData->InterruptTime.High1Time;
233 CurrentTime.LowPart = SharedUserData->InterruptTime.LowPart;
234 if (CurrentTime.HighPart ==
235 SharedUserData->InterruptTime.High2Time) break;
236 YieldProcessor();
237 }
238
239 /* Return the time value */
240 return CurrentTime.QuadPart;
241 }
242 #endif
243
244 /*
245 * @implemented
246 */
247 VOID
248 NTAPI
249 KeSetTimeIncrement(IN ULONG MaxIncrement,
250 IN ULONG MinIncrement)
251 {
252 /* Set some Internal Variables */
253 KeMaximumIncrement = MaxIncrement;
254 KeMinimumIncrement = max(MinIncrement, 10000);
255 KeTimeAdjustment = MaxIncrement;
256 KeTimeIncrement = MaxIncrement;
257 KiTickOffset = MaxIncrement;
258 }
259
260 NTSTATUS
261 NTAPI
262 NtQueryTimerResolution(OUT PULONG MinimumResolution,
263 OUT PULONG MaximumResolution,
264 OUT PULONG ActualResolution)
265 {
266 UNIMPLEMENTED;
267 return STATUS_NOT_IMPLEMENTED;
268 }
269
270 NTSTATUS
271 NTAPI
272 NtSetTimerResolution(IN ULONG DesiredResolution,
273 IN BOOLEAN SetResolution,
274 OUT PULONG CurrentResolution)
275 {
276 UNIMPLEMENTED;
277 return STATUS_NOT_IMPLEMENTED;
278 }
279
280 /* EOF */