Пример #1
0
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);
}
Пример #2
0
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;
}
Пример #3
0
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);
    }
}
Пример #4
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;
}
Пример #5
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();
}