// // No Check-in Source Code. // // Do not make this code available to non-Microsoft personnel // without Intel's express permission // /** *** Copyright (C) 1996-97 Intel Corporation. All rights reserved. *** *** The information and source code contained herein is the exclusive *** property of Intel Corporation and may not be disclosed, examined *** or reproduced in whole or in part without explicit written authorization *** from the company. **/ /*++ Copyright (c) 1995 Intel Corporation Module Name: simtimer.c Abstract: This module implements the routines to provide timer (both interval and profile) interrupt support. Author: 14-Apr-1995 Environment: Kernel mode Revision History: --*/ #include "halp.h" #include ULONGLONG HalpUpdateITM( IN ULONGLONG HalpClockCount ); ULONG HalpSetTimeIncrement ( IN ULONG DesiredIncrement ); __declspec(dllimport) BOOLEAN KdPollBreakIn( VOID ); ULONGLONG HalpPerformanceFrequency; static ULONGLONG HalpClockCount; static ULONG HalpCurrentTimeIncrement = DEFAULT_CLOCK_INTERVAL; static ULONG HalpNextTimeIncrement = DEFAULT_CLOCK_INTERVAL; static ULONG HalpNewTimeIncrement = DEFAULT_CLOCK_INTERVAL; #define GAMBIT 1 #ifdef GAMBIT ULONG HalpKdPollDelayCount = 0; #endif static ULONG_PTR HalpProfileInterval = (ULONG_PTR)DEFAULT_PROFILE_INTERVAL; static BOOLEAN HalpProfileStopped = TRUE; ULONG HalSetTimeIncrement ( IN ULONG DesiredIncrement ) /*++ Routine Description: This routine initialize system time clock to generate an interrupt at every DesiredIncrement interval. It calls the SSC function SscSetPeriodicInterruptInterval to set the new interval. The new interval takes effect after the next timer interval interrupt is deliverd. Arguments: DesiredIncrement - desired interval between every timer tick (in 100ns unit.) Return Value: The time increment set. --*/ { SscSetPeriodicInterruptInterval ( SSC_CLOCK_TIMER_INTERRUPT, 100 * DesiredIncrement ); HalpNextTimeIncrement = DesiredIncrement; HalpSetTimeIncrement(DesiredIncrement); return DesiredIncrement; } VOID HalStartProfileInterrupt( IN KPROFILE_SOURCE ProfileSource ) /*++ Routine Description: This routine calls SscSetPeriodicInterruptInterval to request the simulator to send profile timer interrupt to the OS at the interval specified by HalpProfileInterval. It also sets HalpProfileStopped to FALSE. Arguments: Reserved Return Value: Returns nothing. --*/ { SscSetPeriodicInterruptInterval ( SSC_PROFILE_TIMER_INTERRUPT, 100 * (ULONG)HalpProfileInterval ); HalpProfileStopped = FALSE; } VOID HalStopProfileInterrupt( IN KPROFILE_SOURCE ProfileSource ) /*++ Routine Description: What we do here is change the interrupt interval to 0. Essentially, the simulator stop sending profile timer interrupts to the OS. It also sets HalpProfileStopped to TRUE. Arguments: Reserved Return Value: Returns nothing. --*/ { SscSetPeriodicInterruptInterval (SSC_PROFILE_TIMER_INTERRUPT, 0); HalpProfileStopped = TRUE; } ULONG_PTR HalSetProfileInterval( IN ULONG_PTR Interval ) /*++ Routine Description: This procedure sets the interrupt rate (and thus the sampling interval) for the profiling interrupt. If profiling is active (HalpProfileStopped == FALSE), the SSC function SscSetPeriodicInterruptInterval is called to set the new profile timer interrupt interval. Otherwise, a simple rate validation computation is done. Arguments: Interval - Time interval in 100ns units. Return Value: Time interval actually used by the system. --*/ { // // If the specified profile interval is less that the minimum profile // interval or greater than the maximum profile interval, then set the // profile interval to the minimum or maximum as appropriate. // // // Check to see if the Desired Interval is reasonable, if not adjust it. // We can probably remove this check once we've verified everything works // as anticipated. // if (Interval > MAXIMUM_PROFILE_INTERVAL) { HalpProfileInterval = MAXIMUM_PROFILE_INTERVAL; } else if (Interval < MINIMUM_PROFILE_INTERVAL) { HalpProfileInterval = MINIMUM_PROFILE_INTERVAL; } else { HalpProfileInterval = Interval; } // // Profiling is active. Make the new interrupt interval effective. // if (!HalpProfileStopped) { SscSetPeriodicInterruptInterval ( SSC_PROFILE_TIMER_INTERRUPT, 100 * (ULONG)HalpProfileInterval ); } return HalpProfileInterval; } VOID HalpClockInterrupt ( IN PKINTERRUPT_ROUTINE Interrupt, IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: System Clock Interrupt Handler, for P0 processor only. N.B. Assumptions: Comes with IRQL set to CLOCK_LEVEL to disable interrupts. Arguments: TrapFrame - Trap frame address. Return Value: None. --*/ { // // Call the kernel to update system time. // P0 updates System time and Run Time. // KeUpdateSystemTime(TrapFrame,HalpCurrentTimeIncrement); HalpCurrentTimeIncrement = HalpNextTimeIncrement; HalpNextTimeIncrement = HalpNewTimeIncrement; // // Increment ITM, accounting for interrupt latency. // HalpUpdateITM(HalpClockCount); #ifdef GAMBIT if (!HalpKdPollDelayCount) { HalpKdPollDelayCount = 4; #endif if ( KdDebuggerEnabled && KdPollBreakIn() ) KeBreakinBreakpoint(); #ifdef GAMBIT } else { HalpKdPollDelayCount--; } #endif } VOID HalpClockInterruptPn ( IN PKINTERRUPT_ROUTINE Interrupt, IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: System Clock Interrupt Handler, for processors other than P0. N.B. Assumptions: Comes with IRQL set to CLOCK_LEVEL to disable interrupts. Arguments: TrapFrame - Trap frame address. Return Value: None. --*/ { // // Call the kernel to update run time. // Pn updates only Run time. // KeUpdateRunTime(TrapFrame); // // Increment ITM, accounting for interrupt latency. // HalpUpdateITM(HalpClockCount); } VOID HalpProfileInterrupt ( IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: System Profile Interrupt Handler. Arguments: TrapFrame - Trap frame address. Return Value: None. --*/ { // KeProfileInterrupt (TrapFrame); } ULONG HalpSetTimeIncrement ( IN ULONG DesiredIncrement ) /*++ Routine Description: This function is called to set the clock interrupt rate to the frequency required by the specified time increment value. N.B. This function is only executed on the processor that keeps the system time. This function should eventually become the HalSetTimeIncrement once we actually start using the ITC/ITM. Not currently supported by the simulator. Arguments: DesiredIncrement - Supplies desired number of 100ns units between clock interrupts. Return Value: The actual time increment in 100ns units. --*/ { ULONGLONG NextIntervalCount; KIRQL OldIrql; // // DesiredIncrement must map within the acceptable range. // if (DesiredIncrement < MINIMUM_CLOCK_INTERVAL) DesiredIncrement = MINIMUM_CLOCK_INTERVAL; else if (DesiredIncrement > MAXIMUM_CLOCK_INTERVAL) DesiredIncrement = MAXIMUM_CLOCK_INTERVAL; // // Raise IRQL to the highest level, set the new clock interrupt // parameters, lower IRQl, and return the new time increment value. // KeRaiseIrql(HIGH_LEVEL, &OldIrql); // // Calculate the actual 64 bit time value which forms the target interval. // The resulting value is added to the ITC to form the new ITM value. // HalpPerformanceFrequency is the calibrated value for the ITC whose value // works out to be 100ns (or as close as we can come). // NextIntervalCount = HalpPerformanceFrequency * DesiredIncrement; // // Calculate the number of 100ns units to report to the kernel every // time the ITM matches the ITC with this new period. Note, for small // values of DesiredIncrement (min being 10000, ie 1ms), truncation // in the above may result in a small decrement in the 5th decimal // place. As we are effectively dealing with a 4 digit number, eg // 10000 becomes 9999.something, we really can't do any better than // the following. // HalpClockCount = NextIntervalCount; HalpNewTimeIncrement = DesiredIncrement; KeLowerIrql(OldIrql); return DesiredIncrement; }