VOID KeQuerySystemTime ( OUT PLARGE_INTEGER CurrentTime ) /*++ Routine Description: This function returns the current system time by determining when the time is stable and then returning its value. Arguments: CurrentTime - Supplies a pointer to a variable that will receive the current system time. Return Value: None. --*/ { KiQuerySystemTime(CurrentTime); return; }
VOID FASTCALL KiTimerListExpire ( IN PLIST_ENTRY ExpiredListHead, IN KIRQL OldIrql ) /*++ Routine Description: This function is called to process a list of timers that have expired. N.B. This function is called with the dispatcher database locked and returns with the dispatcher database unlocked. Arguments: ExpiredListHead - Supplies a pointer to a list of timers that have expired. OldIrql - Supplies the previous IRQL. Return Value: None. --*/ { LONG Count; PKDPC Dpc; DPC_ENTRY DpcList[MAXIMUM_DPC_LIST_SIZE]; LONG Index; LARGE_INTEGER Interval; KIRQL OldIrql1; LARGE_INTEGER SystemTime; PKTIMER Timer; // // Capture the timer expiration time. // KiQuerySystemTime(&SystemTime); // // Remove the next timer from the expired timer list, set the state of // the timer to signaled, reinsert the timer in the timer tree if it is // periodic, and optionally call the DPC routine if one is specified. // RestartScan: Count = 0; while (ExpiredListHead->Flink != ExpiredListHead) { Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry); KiRemoveTreeTimer(Timer); Timer->Header.SignalState = 1; if (IsListEmpty(&Timer->Header.WaitListHead) == FALSE) { KiWaitTest(Timer, TIMER_EXPIRE_INCREMENT); } // // If the timer is periodic, then compute the next interval time // and reinsert the timer in the timer tree. // // N.B. Even though the timer insertion is relative, it can still // fail if the period of the timer elapses in between computing // the time and inserting the timer in the table. If this happens, // try again. // if (Timer->Period != 0) { Interval.QuadPart = Int32x32To64(Timer->Period, - 10 * 1000); while (!KiInsertTreeTimer(Timer, Interval)) { ; } } if (Timer->Dpc != NULL) { Dpc = Timer->Dpc; // // If the DPC is explicitly targeted to another processor, then // queue the DPC to the target processor. Otherwise, capture the // DPC parameters for execution on the current processor. // #if defined(NT_UP) DpcList[Count].Dpc = Dpc; DpcList[Count].Routine = Dpc->DeferredRoutine; DpcList[Count].Context = Dpc->DeferredContext; Count += 1; if (Count == MAXIMUM_DPC_LIST_SIZE) { break; } #else if ((Dpc->Number >= MAXIMUM_PROCESSORS) && (((ULONG)Dpc->Number - MAXIMUM_PROCESSORS) != (ULONG)KeGetCurrentProcessorNumber())) { KeInsertQueueDpc(Dpc, ULongToPtr(SystemTime.LowPart), ULongToPtr(SystemTime.HighPart)); } else { DpcList[Count].Dpc = Dpc; DpcList[Count].Routine = Dpc->DeferredRoutine; DpcList[Count].Context = Dpc->DeferredContext; Count += 1; if (Count == MAXIMUM_DPC_LIST_SIZE) { break; } } #endif } } // // Unlock the dispacher database and process DPC list entries. // if (Count != 0) { KiUnlockDispatcherDatabase(DISPATCH_LEVEL); Index = 0; do { #if DBG && (defined(i386) || defined(ALPHA)) // // Reset the dpc tick count. If the tick count handler, // which increments this value, detects that it has crossed // a certain threshold, a breakpoint will be generated. // KeGetCurrentPrcb()->DebugDpcTime = 0; #endif (DpcList[Index].Routine)(DpcList[Index].Dpc, DpcList[Index].Context, ULongToPtr(SystemTime.LowPart), ULongToPtr(SystemTime.HighPart)); Index += 1; } while (Index < Count); // // If processing of the expired timer list was terminated because // the DPC List was full, then process any remaining entries. // if (Count == MAXIMUM_DPC_LIST_SIZE) { KiLockDispatcherDatabase(&OldIrql1); goto RestartScan; } KeLowerIrql(OldIrql); } else { KiUnlockDispatcherDatabase(OldIrql); } return; }
LOGICAL FASTCALL KiInsertTreeTimer ( IN PRKTIMER Timer, IN LARGE_INTEGER Interval ) /*++ Routine Description: This function inserts a timer object in the timer queue. N.B. This routine assumes that the dispatcher data lock has been acquired. Arguments: Timer - Supplies a pointer to a dispatcher object of type timer. Interval - Supplies the absolute or relative time at which the time is to expire. Return Value: If the timer is inserted in the timer tree, than a value of TRUE is returned. Otherwise, a value of FALSE is returned. --*/ { LARGE_INTEGER CurrentTime; LARGE_INTEGER SystemTime; LARGE_INTEGER TimeDifference; // // Clear the signal state of timer if the timer period is zero and set // the inserted state to TRUE. // Timer->Header.Inserted = TRUE; Timer->Header.Absolute = FALSE; if (Timer->Period == 0) { Timer->Header.SignalState = FALSE; } // // If the specified interval is not a relative time (i.e., is an absolute // time), then convert it to relative time. // if (Interval.HighPart >= 0) { KiQuerySystemTime(&SystemTime); TimeDifference.QuadPart = SystemTime.QuadPart - Interval.QuadPart; // // If the resultant relative time is greater than or equal to zero, // then the timer has already expired. // if (TimeDifference.HighPart >= 0) { Timer->Header.SignalState = TRUE; Timer->Header.Inserted = FALSE; return FALSE; } Interval = TimeDifference; Timer->Header.Absolute = TRUE; } // // Get the current interrupt time, insert the timer in the timer table, // and return the inserted state. // KiQueryInterruptTime(&CurrentTime); return KiInsertTimerTable(Interval, CurrentTime, Timer); }
VOID KeSetSystemTime ( IN PLARGE_INTEGER NewTime, OUT PLARGE_INTEGER OldTime, IN BOOLEAN AdjustInterruptTime, IN PLARGE_INTEGER HalTimeToSet OPTIONAL ) /*++ Routine Description: This function sets the system time to the specified value and updates timer queue entries to reflect the difference between the old system time and the new system time. Arguments: NewTime - Supplies a pointer to a variable that specifies the new system time. OldTime - Supplies a pointer to a variable that will receive the previous system time. AdjustInterruptTime - If TRUE the amount of time being adjusted is also applied to InterruptTime and TickCount. HalTimeToSet - Supplies an optional time that if specified is to be used to set the time in the realtime clock. Return Value: None. --*/ { LIST_ENTRY AbsoluteListHead; LIST_ENTRY ExpiredListHead; ULONG Hand; ULONG Index; PLIST_ENTRY ListHead; PKSPIN_LOCK_QUEUE LockQueue; PLIST_ENTRY NextEntry; KIRQL OldIrql1; KIRQL OldIrql2; LARGE_INTEGER TimeDelta; TIME_FIELDS TimeFields; PKTIMER Timer; ASSERT((NewTime->HighPart & 0xf0000000) == 0); ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); // // If a realtime clock value is specified, then convert the time value // to time fields. // if (ARGUMENT_PRESENT(HalTimeToSet)) { RtlTimeToTimeFields(HalTimeToSet, &TimeFields); } // // Set affinity to the processor that keeps the system time, raise IRQL // to dispatcher level and lock the dispatcher database, then raise IRQL // to HIGH_LEVEL to synchronize with the clock interrupt routine. // KeSetSystemAffinityThread((KAFFINITY)1); KiLockDispatcherDatabase(&OldIrql1); KeRaiseIrql(HIGH_LEVEL, &OldIrql2); // // Save the previous system time, set the new system time, and set // the realtime clock, if a time value is specified. // KiQuerySystemTime(OldTime); #if defined(_AMD64_) SharedUserData->SystemTime.High2Time = NewTime->HighPart; *((volatile ULONG64 *)&SharedUserData->SystemTime) = NewTime->QuadPart; #else SharedUserData->SystemTime.High2Time = NewTime->HighPart; SharedUserData->SystemTime.LowPart = NewTime->LowPart; SharedUserData->SystemTime.High1Time = NewTime->HighPart; #endif if (ARGUMENT_PRESENT(HalTimeToSet)) { ExCmosClockIsSane = HalSetRealTimeClock(&TimeFields); } // // Compute the difference between the previous system time and the new // system time. // TimeDelta.QuadPart = NewTime->QuadPart - OldTime->QuadPart; // // Update the boot time to reflect the delta. This keeps time based // on boot time constant // KeBootTime.QuadPart = KeBootTime.QuadPart + TimeDelta.QuadPart; // // Track the overall bias applied to the boot time. // KeBootTimeBias = KeBootTimeBias + TimeDelta.QuadPart; // // Lower IRQL to dispatch level and if needed adjust the physical // system interrupt time. // KeLowerIrql(OldIrql2); if (AdjustInterruptTime != FALSE) { AdjustInterruptTime = KeAdjustInterruptTime(TimeDelta.QuadPart); } // // If the physical interrupt time of the system was not adjusted, then // recompute any absolute timers in the system for the new system time. // if (AdjustInterruptTime == FALSE) { // // Acquire the timer table lock, remove all absolute timers from the // timer queue so their due time can be recomputed, and release the // timer table lock. // InitializeListHead(&AbsoluteListHead); for (Index = 0; Index < TIMER_TABLE_SIZE; Index += 1) { ListHead = &KiTimerTableListHead[Index].Entry; LockQueue = KiAcquireTimerTableLock(Index); NextEntry = ListHead->Flink; while (NextEntry != ListHead) { Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); NextEntry = NextEntry->Flink; if (Timer->Header.Absolute != FALSE) { KiRemoveEntryTimer(Timer); InsertTailList(&AbsoluteListHead, &Timer->TimerListEntry); } } KiReleaseTimerTableLock(LockQueue); } // // Recompute the due time and reinsert all absolute timers in the timer // tree. If a timer has already expired, then insert the timer in the // expired timer list. // InitializeListHead(&ExpiredListHead); while (AbsoluteListHead.Flink != &AbsoluteListHead) { Timer = CONTAINING_RECORD(AbsoluteListHead.Flink, KTIMER, TimerListEntry); RemoveEntryList(&Timer->TimerListEntry); Timer->DueTime.QuadPart -= TimeDelta.QuadPart; Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); Timer->Header.Hand = (UCHAR)Hand; LockQueue = KiAcquireTimerTableLock(Hand); if (KiInsertTimerTable(Timer, Hand) == TRUE) { KiRemoveEntryTimer(Timer); InsertTailList(&ExpiredListHead, &Timer->TimerListEntry); } KiReleaseTimerTableLock(LockQueue); } // // If any of the attempts to reinsert a timer failed, then timers have // already expired and must be processed. // // N.B. The following function returns with the dispatcher database // unlocked. // KiTimerListExpire(&ExpiredListHead, OldIrql1); } else { KiUnlockDispatcherDatabase(OldIrql1); } // // Set affinity back to its original value. // KeRevertToUserAffinityThread(); return; }