Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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();
}