ReactOS  0.4.15-dev-1070-ge1a01de
timer.c
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS HAL
3  * LICENSE: GPL - See COPYING in the top level directory
4  * PURPOSE: HAL Timer Routines
5  * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
6  * Timo Kreuzer (timo.kreuzer@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <hal.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 #if defined(ALLOC_PRAGMA) && !defined(_MINIHAL_)
17 #pragma alloc_text(INIT, HalpInitializeClock)
18 #endif
19 
20 /* GLOBALS *******************************************************************/
21 
22 #define PIT_LATCH 0x00
23 
25 
34 
35 /* PRIVATE FUNCTIONS *********************************************************/
36 
38 ULONG
40 {
41  ULONG TimerValue;
42 
43  /* Send counter latch command for channel 0 */
45  __nop();
46 
47  /* Read the value, LSB first */
48  TimerValue = __inbyte(TIMER_CHANNEL0_DATA_PORT);
49  __nop();
50  TimerValue |= __inbyte(TIMER_CHANNEL0_DATA_PORT) << 8;
51 
52  return TimerValue;
53 }
54 
55 VOID
56 NTAPI
58 {
60  TIMER_CONTROL_PORT_REGISTER TimerControl;
61 
62  /* Disable interrupts */
63  Flags = __readeflags();
64  _disable();
65 
66  /* Program the PIT for binary mode */
67  TimerControl.BcdMode = FALSE;
68 
69  /*
70  * Program the PIT to generate a normal rate wave (Mode 2) on channel 0.
71  * Channel 0 is used for the IRQ0 clock interval timer, and channel
72  * 1 is used for DRAM refresh.
73  *
74  * Mode 2 gives much better accuracy than Mode 3.
75  */
76  TimerControl.OperatingMode = PitOperatingMode2;
77  TimerControl.Channel = PitChannel0;
78 
79  /* Set the access mode that we'll use to program the reload value */
80  TimerControl.AccessMode = PitAccessModeLowHigh;
81 
82  /* Now write the programming bits */
83  __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits);
84 
85  /* Next we write the reload value for channel 0 */
86  __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver & 0xFF);
87  __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver >> 8);
88 
89  /* Restore interrupts if they were previously enabled */
91 }
92 
93 INIT_FUNCTION
94 VOID
95 NTAPI
97 {
99  USHORT RollOver;
100 
101  DPRINT("HalpInitializeClock()\n");
102 
103 #if defined(SARCH_PC98)
105 #endif
106 
107  /* Get increment and rollover for the largest time clock ms possible */
109  RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].RollOver;
110 
111  /* Set the maximum and minimum increment with the kernel */
113 
114  /* Set the rollover value for the timer */
115  HalpSetTimerRollOver(RollOver);
116 
117  /* Save rollover and increment */
118  HalpCurrentRollOver = RollOver;
120 }
121 
122 #ifdef _M_IX86
123 #ifndef _MINIHAL_
124 VOID
125 FASTCALL
127 {
128  ULONG LastIncrement;
129  KIRQL Irql;
130 
131  /* Enter trap */
132  KiEnterInterruptTrap(TrapFrame);
133 
134  /* Start the interrupt */
136  {
137  /* Update the performance counter */
140 
141  /* Save increment */
142  LastIncrement = HalpCurrentTimeIncrement;
143 
144  /* Check if someone changed the time rate */
145  if (HalpClockSetMSRate)
146  {
147  /* Update the global values */
150 
151  /* Set new timer rollover */
153 
154  /* We're done */
156  }
157 
158  /* Update the system time -- the kernel will exit this trap */
159  KeUpdateSystemTime(TrapFrame, LastIncrement, Irql);
160  }
161 
162  /* Spurious, just end the interrupt */
163  KiEoiHelper(TrapFrame);
164 }
165 
166 VOID
167 FASTCALL
169 {
170  KIRQL Irql;
171 
172  /* Enter trap */
173  KiEnterInterruptTrap(TrapFrame);
174 
175  /* Start the interrupt */
177  {
178 #if defined(SARCH_PC98)
179  /* Clear the interrupt flag */
183 #else
184  /* Spin until the interrupt pending bit is clear */
187  NOTHING;
189 #endif
190 
191  /* If profiling is enabled, call the kernel function */
193  {
194  KeProfileInterrupt(TrapFrame);
195  }
196 
197  /* Finish the interrupt */
198  _disable();
199  HalEndSystemInterrupt(Irql, TrapFrame);
200  }
201 
202  /* Spurious, just end the interrupt */
203  KiEoiHelper(TrapFrame);
204 }
205 #endif /* !_MINIHAL_ */
206 
207 #endif /* _M_IX86 */
208 
209 /* PUBLIC FUNCTIONS ***********************************************************/
210 
211 /*
212  * @implemented
213  */
214 VOID
215 NTAPI
217  IN ULONGLONG NewCount)
218 {
220 
221  /* Disable interrupts */
222  Flags = __readeflags();
223  _disable();
224 
225  /* Do a decrement for this CPU */
227 
228  /* Wait for other CPUs */
229  while (*Count);
230 
231  /* Restore interrupts if they were previously enabled */
233 }
234 
235 /*
236  * @implemented
237  */
238 ULONG
239 NTAPI
241 {
242  /* Round increment to ms */
243  Increment /= 10000;
244 
245  /* Normalize between our minimum (1 ms) and maximum (variable) setting */
247  if (Increment <= 0) Increment = 1;
248 
249  /* Set the rate and tell HAL we want to change it */
252 
253  /* Return the increment */
254  return HalpRolloverTable[Increment - 1].Increment;
255 }
256 
258 NTAPI
260 {
261  LARGE_INTEGER CurrentPerfCounter;
262  ULONG CounterValue, ClockDelta;
263  KIRQL OldIrql;
264 
265  /* If caller wants performance frequency, return hardcoded value */
266  if (PerformanceFrequency) PerformanceFrequency->QuadPart = PIT_FREQUENCY;
267 
268  /* Check if we were called too early */
269  if (HalpCurrentRollOver == 0) return HalpPerfCounter;
270 
271  /* Check if interrupts are disabled */
273 
274  /* Raise irql to DISPATCH_LEVEL */
277 
278  do
279  {
280  /* Get the current performance counter value */
281  CurrentPerfCounter = HalpPerfCounter;
282 
283  /* Read the 8254 counter value */
284  CounterValue = HalpRead8254Value();
285 
286  /* Repeat if the value has changed (a clock interrupt happened) */
287  } while (CurrentPerfCounter.QuadPart != HalpPerfCounter.QuadPart);
288 
289  /* After someone changed the clock rate, during the first clock cycle we
290  might see a counter value larger than the rollover. In this case we
291  pretend it already has the new rollover value. */
292  if (CounterValue > HalpCurrentRollOver) CounterValue = HalpCurrentRollOver;
293 
294  /* The interrupt is issued on the falling edge of the OUT line, when the
295  counter changes from 1 to max. Calculate a clock delta, so that directly
296  after the interrupt it is 0, going up to (HalpCurrentRollOver - 1). */
297  ClockDelta = HalpCurrentRollOver - CounterValue;
298 
299  /* Add the clock delta */
300  CurrentPerfCounter.QuadPart += ClockDelta;
301 
302  /* Check if the value is smaller then before, this means, we somehow
303  missed an interrupt. This is a sign that the timer interrupt
304  is very inaccurate. Probably a virtual machine. */
305  if (CurrentPerfCounter.QuadPart < HalpLastPerfCounter.QuadPart)
306  {
307  /* We missed an interrupt. Assume we will receive it later */
308  CurrentPerfCounter.QuadPart += HalpCurrentRollOver;
309  }
310 
311  /* Update the last counter value */
312  HalpLastPerfCounter = CurrentPerfCounter;
313 
314  /* Restore previous irql */
316 
317  /* Return the result */
318  return CurrentPerfCounter;
319 }
320 
321 /* EOF */
#define KeGetCurrentIrql()
Definition: env_spec_w32.h:706
VOID NTAPI HalCalibratePerformanceCounter(IN volatile PLONG Count, IN ULONGLONG NewCount)
Definition: timer.c:90
BOOLEAN HalpProfilingStopped
Definition: profil.c:18
#define PIC_RTC_IRQ
Definition: halhw.h:157
#define IN
Definition: typedefs.h:39
VOID NTAPI HalEndSystemInterrupt(IN KIRQL OldIrql, IN PKTRAP_FRAME TrapFrame)
Definition: pic.c:335
LARGE_INTEGER NTAPI KeQueryPerformanceCounter(IN PLARGE_INTEGER PerformanceFreq)
Definition: timer.c:138
VOID FASTCALL HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
Definition: rtctimer.c:135
VOID NTAPI KeSetTimeIncrement(IN ULONG MaxIncrement, IN ULONG MinIncrement)
Definition: ntoskrnl.c:42
#define TRUE
Definition: types.h:120
VOID FASTCALL KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame, IN ULONG Increment, IN KIRQL OldIrql)
Definition: time.c:64
#define PRIMARY_VECTOR_BASE
Definition: halp.h:16
VOID NTAPI HalpInitializeClockPc98(VOID)
Definition: clock.c:62
_Inout_ __drv_aliasesMem PSLIST_ENTRY _Inout_ PSLIST_ENTRY _In_ ULONG Count
Definition: exfuncs.h:1223
BOOLEAN NTAPI HalBeginSystemInterrupt(IN KIRQL Irql, IN ULONG Vector, OUT PKIRQL OldIrql)
Definition: pic.c:321
VOID NTAPI KeProfileInterrupt(IN PKTRAP_FRAME TrapFrame)
Definition: profobj.c:296
BOOLEAN HalpClockSetMSRate
Definition: timer.c:17
PPC_QUAL void __outbyte(unsigned long const Port, const unsigned char Data)
Definition: intrin_ppc.h:605
#define FASTCALL
Definition: nt_native.h:50
_Out_ PKIRQL Irql
Definition: csq.h:179
BOOLEAN KiEnableTimerWatchdog
Definition: timerobj.c:20
DECLSPEC_NORETURN VOID FASTCALL KiEoiHelper(IN PKTRAP_FRAME TrapFrame)
Definition: traphdlr.c:126
uint32_t ULONG_PTR
Definition: typedefs.h:65
UCHAR KIRQL
Definition: env_spec_w32.h:591
_Must_inspect_result_ _In_ ULONG Flags
Definition: wsk.h:170
__INTRIN_INLINE uintptr_t __readeflags(void)
Definition: intrin_x86.h:1569
ULONG HalpNextMSRate
Definition: timer.c:20
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
#define FALSE
Definition: types.h:117
VOID NTAPI HalpReleaseCmosSpinLock(VOID)
Definition: spinlock.c:244
ULONG HalpPerfCounterCutoff
Definition: timer.c:28
VOID FASTCALL HalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame)
Definition: rtctimer.c:176
unsigned char BOOLEAN
long __cdecl _InterlockedDecrement(_Interlocked_operand_ long volatile *_Addend)
#define TIMER_CONTROL_PORT
Definition: halp.h:22
#define FORCEINLINE
Definition: ntbasedef.h:216
UCHAR FORCEINLINE HalpReadCmos(IN UCHAR Reg)
Definition: cmos.c:24
void DPRINT(...)
Definition: polytest.cpp:61
LARGE_INTEGER HalpRolloverTable[15]
Definition: timer.c:23
KIRQL FASTCALL KfRaiseIrql(IN KIRQL NewIrql)
Definition: pic.c:187
uint64_t ULONGLONG
Definition: typedefs.h:67
#define CLOCK2_LEVEL
Definition: env_spec_w32.h:700
#define RTC_IO_i_INTERRUPT_RESET
Definition: rtc.h:44
#define TIMER_CHANNEL0_DATA_PORT
Definition: halhw.h:67
VOID NTAPI HalpAcquireCmosSpinLock(VOID)
Definition: spinlock.c:227
VOID HalpInitializeClock(VOID)
Definition: timer.c:54
ULONG HalpCurrentRollOver
Definition: timer.c:19
#define PIT_LATCH
Definition: timer.c:22
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:790
__INTRIN_INLINE void __writeeflags(uintptr_t Value)
Definition: intrin_x86.h:1564
#define VOID
Definition: acefi.h:82
#define NOTHING
Definition: env_spec_w32.h:461
#define RTC_REGISTER_C
Definition: halhw.h:17
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
void __nop(void)
Definition: intrin_x86.h:1953
VOID FASTCALL KfLowerIrql(IN KIRQL NewIrql)
Definition: pic.c:232
ULONG PIT_FREQUENCY
Definition: clock.c:17
FORCEINLINE VOID KiEnterInterruptTrap(IN PKTRAP_FRAME TrapFrame)
Definition: trap_x.h:368
LARGE_INTEGER HalpPerfCounter
Definition: timer.c:27
#define PIC_TIMER_IRQ
Definition: halhw.h:155
unsigned short USHORT
Definition: pedump.c:61
#define PROFILE_LEVEL
Definition: env_spec_w32.h:698
ULONG HalpCurrentTimeIncrement
Definition: timer.c:25
LARGE_INTEGER HalpLastPerfCounter
Definition: timer.c:26
void __cdecl _disable(void)
Definition: intrin_arm.h:365
ULONG HalpLargestClockMS
Definition: timer.c:21
unsigned int ULONG
Definition: retypes.h:1
#define RTC_REG_C_IRQ
Definition: halhw.h:18
VOID NTAPI HalpSetTimerRollOver(USHORT RollOver)
Definition: timer.c:57
FORCEINLINE ULONG HalpRead8254Value(void)
Definition: timer.c:39
IN OUT PLONG IN OUT PLONG Addend IN OUT PLONG IN LONG Increment
Definition: CrNtStubs.h:42
signed int * PLONG
Definition: retypes.h:5
ULONG NTAPI HalSetTimeIncrement(IN ULONG Increment)
Definition: timer.c:102
LONGLONG QuadPart
Definition: typedefs.h:114
#define EFLAGS_INTERRUPT_MASK
Definition: ketypes.h:126
PPC_QUAL unsigned char __inbyte(const unsigned long Port)
Definition: intrin_ppc.h:539