VOID FASTCALL KiCompleteTimer(IN PKTIMER Timer, IN PKSPIN_LOCK_QUEUE LockQueue) { LIST_ENTRY ListHead; BOOLEAN RequestInterrupt = FALSE; DPRINT("KiCompleteTimer(): Timer %p, LockQueue: %p\n", Timer, LockQueue); /* Remove it from the timer list */ KiRemoveEntryTimer(Timer); /* Link the timer list to our stack */ ListHead.Flink = &Timer->TimerListEntry; ListHead.Blink = &Timer->TimerListEntry; Timer->TimerListEntry.Flink = &ListHead; Timer->TimerListEntry.Blink = &ListHead; /* Release the timer lock */ KiReleaseTimerLock(LockQueue); /* Acquire dispatcher lock */ KiAcquireDispatcherLockAtDpcLevel(); /* Signal the timer if it's still on our list */ if (!IsListEmpty(&ListHead)) RequestInterrupt = KiSignalTimer(Timer); /* Release the dispatcher lock */ KiReleaseDispatcherLockFromDpcLevel(); /* Request a DPC if needed */ if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); }
BOOLEAN FASTCALL KiInsertTreeTimer(IN PKTIMER Timer, IN LARGE_INTEGER Interval) { BOOLEAN Inserted = FALSE; ULONG Hand = 0; PKSPIN_LOCK_QUEUE LockQueue; DPRINT("KiInsertTreeTimer(): Timer %p, Interval: %I64d\n", Timer, Interval.QuadPart); /* Setup the timer's due time */ if (KiComputeDueTime(Timer, Interval, &Hand)) { /* Acquire the lock */ LockQueue = KiAcquireTimerLock(Hand); /* Insert the timer */ if (KiInsertTimerTable(Timer, Hand)) { /* It was already there, remove it */ KiRemoveEntryTimer(Timer); Timer->Header.Inserted = FALSE; } else { /* Otherwise, we're now inserted */ Inserted = TRUE; } /* Release the lock */ KiReleaseTimerLock(LockQueue); } /* Release the lock and return insert status */ return Inserted; }
VOID NTAPI KiTimerExpiration(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { ULARGE_INTEGER SystemTime, InterruptTime; LARGE_INTEGER Interval; LONG Limit, Index, i; ULONG Timers, ActiveTimers, DpcCalls; PLIST_ENTRY ListHead, NextEntry; KIRQL OldIrql; PKTIMER Timer; PKDPC TimerDpc; ULONG Period; DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; PKSPIN_LOCK_QUEUE LockQueue; #ifdef CONFIG_SMP PKPRCB Prcb = KeGetCurrentPrcb(); #endif /* Disable interrupts */ _disable(); /* Query system and interrupt time */ KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); InterruptTime.QuadPart = KeQueryInterruptTime(); Limit = KeTickCount.LowPart; /* Bring interrupts back */ _enable(); /* Get the index of the timer and normalize it */ Index = PtrToLong(SystemArgument1); if ((Limit - Index) >= TIMER_TABLE_SIZE) { /* Normalize it */ Limit = Index + TIMER_TABLE_SIZE - 1; } /* Setup index and actual limit */ Index--; Limit &= (TIMER_TABLE_SIZE - 1); /* Setup accounting data */ DpcCalls = 0; Timers = 24; ActiveTimers = 4; /* Lock the Database and Raise IRQL */ OldIrql = KiAcquireDispatcherLock(); /* Start expiration loop */ do { /* Get the current index */ Index = (Index + 1) & (TIMER_TABLE_SIZE - 1); /* Get list pointers and loop the list */ ListHead = &KiTimerTableListHead[Index].Entry; while (ListHead != ListHead->Flink) { /* Lock the timer and go to the next entry */ LockQueue = KiAcquireTimerLock(Index); NextEntry = ListHead->Flink; /* Get the current timer and check its due time */ Timers--; Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); if ((NextEntry != ListHead) && (Timer->DueTime.QuadPart <= InterruptTime.QuadPart)) { /* It's expired, remove it */ ActiveTimers--; KiRemoveEntryTimer(Timer); /* Make it non-inserted, unlock it, and signal it */ Timer->Header.Inserted = FALSE; KiReleaseTimerLock(LockQueue); Timer->Header.SignalState = 1; /* Get the DPC and period */ TimerDpc = Timer->Dpc; Period = Timer->Period; /* Check if there's any waiters */ if (!IsListEmpty(&Timer->Header.WaitListHead)) { /* Check the type of event */ if (Timer->Header.Type == TimerNotificationObject) { /* Unwait the thread */ KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); } else { /* Otherwise unwait the thread and signal the timer */ KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); } } /* Check if we have a period */ if (Period) { /* Calculate the interval and insert the timer */ Interval.QuadPart = Int32x32To64(Period, -10000); while (!KiInsertTreeTimer(Timer, Interval)); } /* Check if we have a DPC */ if (TimerDpc) { #ifdef CONFIG_SMP /* * If the DPC is targeted to another processor, * then insert it into that processor's DPC queue * instead of delivering it now. * If the DPC is a threaded DPC, and the current CPU * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs), * then also insert it into the DPC queue for threaded delivery, * instead of doing it here. */ if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) && ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) || ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))) { /* Queue it */ KeInsertQueueDpc(TimerDpc, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } else #endif { /* Setup the DPC Entry */ DpcEntry[DpcCalls].Dpc = TimerDpc; DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; DpcCalls++; ASSERT(DpcCalls < MAX_TIMER_DPCS); } } /* Check if we're done processing */ if (!(ActiveTimers) || !(Timers)) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Reset accounting */ Timers = 24; ActiveTimers = 4; /* Lock the dispatcher database */ KiAcquireDispatcherLock(); } } else { /* Check if the timer list is empty */ if (NextEntry != ListHead) { /* Sanity check */ ASSERT(KiTimerTableListHead[Index].Time.QuadPart <= Timer->DueTime.QuadPart); /* Update the time */ _disable(); KiTimerTableListHead[Index].Time.QuadPart = Timer->DueTime.QuadPart; _enable(); } /* Release the lock */ KiReleaseTimerLock(LockQueue); /* Check if we've scanned all the timers we could */ if (!Timers) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Reset accounting */ Timers = 24; ActiveTimers = 4; /* Lock the dispatcher database */ KiAcquireDispatcherLock(); } /* Done looping */ break; } } } while (Index != Limit); /* Verify the timer table, on debug builds */ if (KeNumberProcessors == 1) KiCheckTimerTable(InterruptTime); /* Check if we still have DPC entries */ if (DpcCalls) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Lower IRQL if we need to */ if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql); } else { /* Unlock the dispatcher */ KiReleaseDispatcherLock(OldIrql); } }
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; }
VOID NTAPI KeSetSystemTime(IN PLARGE_INTEGER NewTime, OUT PLARGE_INTEGER OldTime, IN BOOLEAN FixInterruptTime, IN PLARGE_INTEGER HalTime OPTIONAL) { TIME_FIELDS TimeFields; KIRQL OldIrql, OldIrql2; LARGE_INTEGER DeltaTime; PLIST_ENTRY ListHead, NextEntry; PKTIMER Timer; PKSPIN_LOCK_QUEUE LockQueue; LIST_ENTRY TempList, TempList2; ULONG Hand, i; /* Sanity checks */ ASSERT((NewTime->HighPart & 0xF0000000) == 0); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); /* Check if this is for the HAL */ if (HalTime) RtlTimeToTimeFields(HalTime, &TimeFields); /* Set affinity to this CPU, lock the dispatcher, and raise IRQL */ KeSetSystemAffinityThread(1); OldIrql = KiAcquireDispatcherLock(); KeRaiseIrql(HIGH_LEVEL, &OldIrql2); /* Query the system time now */ KeQuerySystemTime(OldTime); /* Set the new system time (ordering of these operations is critical) */ SharedUserData->SystemTime.High2Time = NewTime->HighPart; SharedUserData->SystemTime.LowPart = NewTime->LowPart; SharedUserData->SystemTime.High1Time = NewTime->HighPart; /* Check if this was for the HAL and set the RTC time */ if (HalTime) ExCmosClockIsSane = HalSetRealTimeClock(&TimeFields); /* Calculate the difference between the new and the old time */ DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart; /* Update system boot time */ KeBootTime.QuadPart += DeltaTime.QuadPart; KeBootTimeBias = KeBootTimeBias + DeltaTime.QuadPart; /* Lower IRQL back */ KeLowerIrql(OldIrql2); /* Check if we need to adjust interrupt time */ if (FixInterruptTime) ASSERT(FALSE); /* Setup a temporary list of absolute timers */ InitializeListHead(&TempList); /* Loop current timers */ for (i = 0; i < TIMER_TABLE_SIZE; i++) { /* Loop the entries in this table and lock the timers */ ListHead = &KiTimerTableListHead[i].Entry; LockQueue = KiAcquireTimerLock(i); NextEntry = ListHead->Flink; while (NextEntry != ListHead) { /* Get the timer */ Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); NextEntry = NextEntry->Flink; /* Is it absolute? */ if (Timer->Header.Absolute) { /* Remove it from the timer list */ KiRemoveEntryTimer(Timer); /* Insert it into our temporary list */ InsertTailList(&TempList, &Timer->TimerListEntry); } } /* Release the lock */ KiReleaseTimerLock(LockQueue); } /* Setup a temporary list of expired timers */ InitializeListHead(&TempList2); /* Loop absolute timers */ while (TempList.Flink != &TempList) { /* Get the timer */ Timer = CONTAINING_RECORD(TempList.Flink, KTIMER, TimerListEntry); RemoveEntryList(&Timer->TimerListEntry); /* Update the due time and handle */ Timer->DueTime.QuadPart -= DeltaTime.QuadPart; Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); Timer->Header.Hand = (UCHAR)Hand; /* Lock the timer and re-insert it */ LockQueue = KiAcquireTimerLock(Hand); if (KiInsertTimerTable(Timer, Hand)) { /* Remove it from the timer list */ KiRemoveEntryTimer(Timer); /* Insert it into our temporary list */ InsertTailList(&TempList2, &Timer->TimerListEntry); } /* Release the lock */ KiReleaseTimerLock(LockQueue); } /* Process expired timers. This releases the dispatcher lock. */ KiTimerListExpire(&TempList2, OldIrql); /* Revert affinity */ KeRevertToUserAffinityThread(); }