xref: /reactos/ntoskrnl/ke/clock.c (revision c2c66aff)
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, 0, 0 };
20 ULONG KeMaximumIncrement;
21 ULONG KeMinimumIncrement;
22 ULONG KeTimeIncrement;
23 
24 /* PRIVATE FUNCTIONS *********************************************************/
25 
26 VOID
27 NTAPI
KeSetSystemTime(IN PLARGE_INTEGER NewTime,OUT PLARGE_INTEGER OldTime,IN BOOLEAN FixInterruptTime,IN PLARGE_INTEGER HalTime OPTIONAL)28 KeSetSystemTime(IN PLARGE_INTEGER NewTime,
29                 OUT PLARGE_INTEGER OldTime,
30                 IN BOOLEAN FixInterruptTime,
31                 IN PLARGE_INTEGER HalTime OPTIONAL)
32 {
33     TIME_FIELDS TimeFields;
34     KIRQL OldIrql, OldIrql2;
35     LARGE_INTEGER DeltaTime;
36     PLIST_ENTRY ListHead, NextEntry;
37     PKTIMER Timer;
38     PKSPIN_LOCK_QUEUE LockQueue;
39     LIST_ENTRY TempList, TempList2;
40     ULONG Hand, i;
41 
42     /* Sanity checks */
43     ASSERT((NewTime->HighPart & 0xF0000000) == 0);
44     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
45 
46     /* Check if this is for the HAL */
47     if (HalTime) RtlTimeToTimeFields(HalTime, &TimeFields);
48 
49     /* Set affinity to this CPU, lock the dispatcher, and raise IRQL */
50     KeSetSystemAffinityThread(1);
51     OldIrql = KiAcquireDispatcherLock();
52     KeRaiseIrql(HIGH_LEVEL, &OldIrql2);
53 
54     /* Query the system time now */
55     KeQuerySystemTime(OldTime);
56 
57     /* Set the new system time (ordering of these operations is critical) */
58     SharedUserData->SystemTime.High2Time = NewTime->HighPart;
59     SharedUserData->SystemTime.LowPart = NewTime->LowPart;
60     SharedUserData->SystemTime.High1Time = NewTime->HighPart;
61 
62     /* Check if this was for the HAL and set the RTC time */
63     if (HalTime) ExCmosClockIsSane = HalSetRealTimeClock(&TimeFields);
64 
65     /* Calculate the difference between the new and the old time */
66     DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart;
67 
68     /* Update system boot time */
69     KeBootTime.QuadPart += DeltaTime.QuadPart;
70     KeBootTimeBias = KeBootTimeBias + DeltaTime.QuadPart;
71 
72     /* Lower IRQL back */
73     KeLowerIrql(OldIrql2);
74 
75     /* Check if we need to adjust interrupt time */
76     if (FixInterruptTime) ASSERT(FALSE);
77 
78     /* Setup a temporary list of absolute timers */
79     InitializeListHead(&TempList);
80 
81     /* Loop current timers */
82     for (i = 0; i < TIMER_TABLE_SIZE; i++)
83     {
84         /* Loop the entries in this table and lock the timers */
85         ListHead = &KiTimerTableListHead[i].Entry;
86         LockQueue = KiAcquireTimerLock(i);
87         NextEntry = ListHead->Flink;
88         while (NextEntry != ListHead)
89         {
90             /* Get the timer */
91             Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
92             NextEntry = NextEntry->Flink;
93 
94             /* Is it absolute? */
95             if (Timer->Header.Absolute)
96             {
97                 /* Remove it from the timer list */
98                 KiRemoveEntryTimer(Timer);
99 
100                 /* Insert it into our temporary list */
101                 InsertTailList(&TempList, &Timer->TimerListEntry);
102             }
103         }
104 
105         /* Release the lock */
106         KiReleaseTimerLock(LockQueue);
107     }
108 
109     /* Setup a temporary list of expired timers */
110     InitializeListHead(&TempList2);
111 
112     /* Loop absolute timers */
113     while (TempList.Flink != &TempList)
114     {
115         /* Get the timer */
116         Timer = CONTAINING_RECORD(TempList.Flink, KTIMER, TimerListEntry);
117         RemoveEntryList(&Timer->TimerListEntry);
118 
119         /* Update the due time and handle */
120         Timer->DueTime.QuadPart -= DeltaTime.QuadPart;
121         Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart);
122         Timer->Header.Hand = (UCHAR)Hand;
123 
124         /* Lock the timer and re-insert it */
125         LockQueue = KiAcquireTimerLock(Hand);
126         if (KiInsertTimerTable(Timer, Hand))
127         {
128             /* Remove it from the timer list */
129             KiRemoveEntryTimer(Timer);
130 
131             /* Insert it into our temporary list */
132             InsertTailList(&TempList2, &Timer->TimerListEntry);
133         }
134 
135         /* Release the lock */
136         KiReleaseTimerLock(LockQueue);
137     }
138 
139     /* Process expired timers. This releases the dispatcher lock. */
140     KiTimerListExpire(&TempList2, OldIrql);
141 
142     /* Revert affinity */
143     KeRevertToUserAffinityThread();
144 }
145 
146 /* PUBLIC FUNCTIONS **********************************************************/
147 
148 /*
149  * @implemented
150  */
151 ULONG
152 NTAPI
KeQueryTimeIncrement(VOID)153 KeQueryTimeIncrement(VOID)
154 {
155     /* Return the increment */
156     return KeMaximumIncrement;
157 }
158 
159 /*
160  * @implemented
161  */
162 #undef KeQueryTickCount
163 VOID
164 NTAPI
KeQueryTickCount(IN PLARGE_INTEGER TickCount)165 KeQueryTickCount(IN PLARGE_INTEGER TickCount)
166 {
167     /* Loop until we get a perfect match */
168     for (;;)
169     {
170         /* Read the tick count value */
171         TickCount->HighPart = KeTickCount.High1Time;
172         TickCount->LowPart = KeTickCount.LowPart;
173         if (TickCount->HighPart == KeTickCount.High2Time) break;
174         YieldProcessor();
175     }
176 }
177 
178 #ifndef _M_AMD64
179 /*
180  * @implemented
181  */
182 VOID
183 NTAPI
KeQuerySystemTime(OUT PLARGE_INTEGER CurrentTime)184 KeQuerySystemTime(OUT PLARGE_INTEGER CurrentTime)
185 {
186     /* Loop until we get a perfect match */
187     for (;;)
188     {
189         /* Read the time value */
190         CurrentTime->HighPart = SharedUserData->SystemTime.High1Time;
191         CurrentTime->LowPart = SharedUserData->SystemTime.LowPart;
192         if (CurrentTime->HighPart ==
193             SharedUserData->SystemTime.High2Time) break;
194         YieldProcessor();
195     }
196 }
197 
198 /*
199  * @implemented
200  */
201 ULONGLONG
202 NTAPI
KeQueryInterruptTime(VOID)203 KeQueryInterruptTime(VOID)
204 {
205     LARGE_INTEGER CurrentTime;
206 
207     /* Loop until we get a perfect match */
208     for (;;)
209     {
210         /* Read the time value */
211         CurrentTime.HighPart = SharedUserData->InterruptTime.High1Time;
212         CurrentTime.LowPart = SharedUserData->InterruptTime.LowPart;
213         if (CurrentTime.HighPart ==
214             SharedUserData->InterruptTime.High2Time) break;
215         YieldProcessor();
216     }
217 
218     /* Return the time value */
219     return CurrentTime.QuadPart;
220 }
221 #endif
222 
223 /*
224  * @implemented
225  */
226 VOID
227 NTAPI
KeSetTimeIncrement(IN ULONG MaxIncrement,IN ULONG MinIncrement)228 KeSetTimeIncrement(IN ULONG MaxIncrement,
229                    IN ULONG MinIncrement)
230 {
231     /* Set some Internal Variables */
232     KeMaximumIncrement = MaxIncrement;
233     KeMinimumIncrement = max(MinIncrement, 10000);
234     KeTimeAdjustment = MaxIncrement;
235     KeTimeIncrement = MaxIncrement;
236     KiTickOffset = MaxIncrement;
237 }
238 
239 /* EOF */
240