You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
860 lines
20 KiB
860 lines
20 KiB
/*++
|
|
|
|
Module Name:
|
|
|
|
mpclockc.c
|
|
|
|
Abstract:
|
|
|
|
Author:
|
|
|
|
Ron Mosgrove - Intel
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
#include "halp.h"
|
|
|
|
//
|
|
// Define global data used to communicate new clock rates to the clock
|
|
// interrupt service routine.
|
|
//
|
|
|
|
struct RtcTimeIncStruc {
|
|
ULONG RTCRegisterA; // The RTC register A value for this rate
|
|
ULONG RateIn100ns; // This rate in multiples of 100ns
|
|
ULONG RateAdjustmentNs; // Error Correction (in ns)
|
|
ULONG RateAdjustmentCnt; // Error Correction (as a fraction of 256)
|
|
ULONG IpiRate; // IPI Rate Count (as a fraction of 256)
|
|
};
|
|
|
|
//
|
|
// The adjustment is expressed in terms of a fraction of 256 so that
|
|
// the ISR can easily determine when a 100ns slice needs to be subtracted
|
|
// from the count passed to the kernel without any expensive operations
|
|
//
|
|
// Using 256 as a base means that anytime the count becomes greater
|
|
// than 256 the time slice must be incremented, the overflow can then
|
|
// be cleared by AND'ing the value with 0xff
|
|
//
|
|
|
|
#define AVAILABLE_INCREMENTS 5
|
|
|
|
struct RtcTimeIncStruc HalpRtcTimeIncrements[AVAILABLE_INCREMENTS] = {
|
|
{0x026, 9766, 38, 96, /* 3/8 of 256 */ 16},
|
|
{0x027, 19532, 75, 192, /* 3/4 of 256 */ 32},
|
|
{0x028, 39063, 50, 128, /* 1/2 of 256 */ 64},
|
|
{0x029, 78125, 0, 0, 128},
|
|
{0x02a, 156250, 0, 0, 256}
|
|
};
|
|
|
|
|
|
ULONG HalpInitialClockRateIndex = AVAILABLE_INCREMENTS-1;
|
|
|
|
extern ULONG HalpCurrentRTCRegisterA;
|
|
extern ULONG HalpCurrentClockRateIn100ns;
|
|
extern ULONG HalpCurrentClockRateAdjustment;
|
|
extern ULONG HalpCurrentIpiRate;
|
|
|
|
extern ULONG HalpNextMSRate;
|
|
|
|
#if !defined(_WIN64)
|
|
extern ULONG HalpClockWork;
|
|
extern BOOLEAN HalpClockSetMSRate;
|
|
#endif
|
|
|
|
|
|
|
|
VOID
|
|
HalpSetInitialClockRate (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
HalpInitializeTimerResolution (
|
|
ULONG Rate
|
|
);
|
|
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGELK, HalpSetInitialClockRate)
|
|
#pragma alloc_text(INIT, HalpInitializeTimerResolution)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
HalpInitializeTimerResolution (
|
|
ULONG Rate
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to initialize the timer resolution to be
|
|
something other then the default. The rate is set to the closest
|
|
supported setting below the requested rate.
|
|
|
|
Arguments:
|
|
|
|
Rate - in 100ns units
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i, s;
|
|
|
|
//
|
|
// Find the table index of the rate to use
|
|
//
|
|
|
|
for (i=1; i < AVAILABLE_INCREMENTS; i++) {
|
|
if (HalpRtcTimeIncrements[i].RateIn100ns > Rate) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
HalpInitialClockRateIndex = i - 1;
|
|
|
|
//
|
|
// Scale IpiRate according to max TimeIncr rate which can be used
|
|
//
|
|
|
|
s = AVAILABLE_INCREMENTS - HalpInitialClockRateIndex - 1;
|
|
for (i=0; i < AVAILABLE_INCREMENTS; i++) {
|
|
HalpRtcTimeIncrements[i].IpiRate <<= s;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
HalpSetInitialClockRate (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to set the initial clock interrupt rate
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
extern ULONG HalpNextMSRate;
|
|
|
|
//
|
|
// On ACPI timer machines, we need to init an index into the
|
|
// milisecond(s) array used by pmtimerc.c's Piix4 workaround
|
|
//
|
|
#ifdef ACPI_HAL
|
|
#ifdef NT_UP
|
|
extern ULONG HalpCurrentMSRateTableIndex;
|
|
|
|
HalpCurrentMSRateTableIndex = (1 << HalpInitialClockRateIndex) - 1;
|
|
|
|
//
|
|
// The Piix4 upper bound table ends at 15ms (index 14), so we'll have
|
|
// to map our 15.6ms entry to it as a special case
|
|
//
|
|
if (HalpCurrentMSRateTableIndex == 0xF) {
|
|
HalpCurrentMSRateTableIndex--;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
HalpNextMSRate = HalpInitialClockRateIndex;
|
|
|
|
HalpCurrentClockRateIn100ns =
|
|
HalpRtcTimeIncrements[HalpNextMSRate].RateIn100ns;
|
|
HalpCurrentClockRateAdjustment =
|
|
HalpRtcTimeIncrements[HalpNextMSRate].RateAdjustmentCnt;
|
|
HalpCurrentRTCRegisterA =
|
|
HalpRtcTimeIncrements[HalpNextMSRate].RTCRegisterA;
|
|
HalpCurrentIpiRate =
|
|
HalpRtcTimeIncrements[HalpNextMSRate].IpiRate;
|
|
|
|
HalpClockWork = 0;
|
|
|
|
KeSetTimeIncrement (
|
|
HalpRtcTimeIncrements[HalpNextMSRate].RateIn100ns,
|
|
HalpRtcTimeIncrements[0].RateIn100ns
|
|
);
|
|
|
|
}
|
|
|
|
|
|
#ifdef MMTIMER
|
|
ULONG
|
|
HalpAcpiTimerSetTimeIncrement(
|
|
IN ULONG DesiredIncrement
|
|
)
|
|
#else
|
|
ULONG
|
|
HalSetTimeIncrement (
|
|
IN ULONG DesiredIncrement
|
|
)
|
|
#endif
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to set the clock interrupt rate to the frequency
|
|
required by the specified time increment value.
|
|
|
|
Arguments:
|
|
|
|
DesiredIncrement - Supplies desired number of 100ns units between clock
|
|
interrupts.
|
|
|
|
Return Value:
|
|
|
|
The actual time increment in 100ns units.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Set the new clock interrupt parameters, return the new time increment value.
|
|
//
|
|
|
|
|
|
for (i=1; i <= HalpInitialClockRateIndex; i++) {
|
|
if (HalpRtcTimeIncrements[i].RateIn100ns > DesiredIncrement) {
|
|
break;
|
|
}
|
|
}
|
|
i = i - 1;
|
|
|
|
KeRaiseIrql(HIGH_LEVEL,&OldIrql);
|
|
|
|
HalpNextMSRate = i + 1;
|
|
HalpClockSetMSRate = TRUE;
|
|
|
|
KeLowerIrql (OldIrql);
|
|
|
|
return (HalpRtcTimeIncrements[i].RateIn100ns);
|
|
}
|
|
|
|
#if defined(_WIN64)
|
|
|
|
#include "..\amd64\halcmn.h"
|
|
|
|
ULONG HalpCurrentMSRateTableIndex;
|
|
|
|
//
|
|
// Forward declared functions
|
|
//
|
|
|
|
VOID
|
|
HalpUpdateTimerWatchdog (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
HalpClockInterruptWork (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
HalpMcaQueueDpc (
|
|
VOID
|
|
);
|
|
|
|
extern PBOOLEAN KdEnteredDebugger;
|
|
extern PVOID HalpTimerWatchdogCurFrame;
|
|
extern PVOID HalpTimerWatchdogLastFrame;
|
|
extern ULONG HalpTimerWatchdogStorageOverflow;
|
|
extern ULONG HalpTimerWatchdogEnabled;
|
|
|
|
//
|
|
// Local constants
|
|
//
|
|
|
|
#define COUNTER_TICKS_FOR_AVG 16
|
|
#define FRAME_COPY_SIZE (64 * sizeof(ULONG))
|
|
|
|
#define APIC_ICR_CLOCK (DELIVER_FIXED | ICR_ALL_EXCL_SELF | APIC_CLOCK_VECTOR)
|
|
|
|
//
|
|
// Flags to tell clock routine when P0 can Ipi other processors
|
|
//
|
|
|
|
ULONG HalpIpiClock = 0;
|
|
|
|
//
|
|
// Timer latency watchdog variables
|
|
//
|
|
|
|
ULONG HalpWatchdogAvgCounter;
|
|
ULONG64 HalpWatchdogCount;
|
|
ULONG64 HalpWatchdogTsc;
|
|
HALP_CLOCKWORK_UNION HalpClockWorkUnion;
|
|
|
|
//
|
|
// Clock rate adjustment counter. This counter is used to keep a tally of
|
|
// adjustments needed to be applied to the RTC rate as passed to the kernel.
|
|
//
|
|
|
|
ULONG HalpCurrentRTCRegisterA;
|
|
ULONG HalpCurrentClockRateIn100ns = 0;
|
|
ULONG HalpCurrentClockRateAdjustment = 0;
|
|
ULONG HalpCurrentIpiRate = 0;
|
|
|
|
ULONG HalpNextMSRate = 0;
|
|
ULONG HalpPendingRate = 0;
|
|
|
|
//
|
|
// Other
|
|
//
|
|
|
|
BOOLEAN HalpUse8254 = FALSE;
|
|
UCHAR HalpSample8254 = 0;
|
|
UCHAR HalpRateAdjustment = 0;
|
|
ULONG HalpIpiRateCounter = 0;
|
|
|
|
VOID
|
|
HalpInitializeClock (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initialize system time clock using RTC to generate an
|
|
interrupt at every 15.6250 ms interval at APIC_CLOCK_VECTOR
|
|
|
|
It also initializes the 8254 if the 8254 is to be used for performance
|
|
counters.
|
|
|
|
See the definition of RegisterAClockValue if clock rate needs to be
|
|
changed.
|
|
|
|
This routine assumes it runs during Phase 0 on P0.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG flags;
|
|
UCHAR regB;
|
|
|
|
if (HalpTimerWatchdogEnabled != 0) {
|
|
HalpWatchdogAvgCounter = COUNTER_TICKS_FOR_AVG;
|
|
HalpWatchdogTsc = ReadTimeStampCounter();
|
|
HalpWatchdogCount = 0;
|
|
}
|
|
|
|
flags = HalpDisableInterrupts();
|
|
|
|
HalpSetInitialClockRate();
|
|
|
|
//
|
|
// Set the interrupt rate to what is actually needed
|
|
//
|
|
|
|
HalpAcquireCmosSpinLock();
|
|
|
|
CMOS_WRITE(CMOS_STATUS_A,(UCHAR)HalpCurrentRTCRegisterA);
|
|
|
|
//
|
|
// Don't clobber the Daylight Savings Time bit in register B, because we
|
|
// stash the LastKnownGood "environment variable" there.
|
|
//
|
|
|
|
regB = CMOS_READ(CMOS_STATUS_B);
|
|
regB &= 1;
|
|
regB |= REGISTER_B_ENABLE_PERIODIC_INTERRUPT;
|
|
|
|
//
|
|
// Write the register B value, then read C and D to initialize
|
|
//
|
|
|
|
CMOS_WRITE(CMOS_STATUS_B,regB);
|
|
CMOS_READ(CMOS_STATUS_C);
|
|
CMOS_READ(CMOS_STATUS_D);
|
|
|
|
HalpReleaseCmosSpinLock();
|
|
|
|
if (HalpUse8254 != FALSE) {
|
|
|
|
HalpAcquireSystemHardwareSpinLock();
|
|
|
|
//
|
|
// Program the 8254 to count down the maximum interval (8254 access
|
|
// is guarded with the system hardware spin lock).
|
|
//
|
|
// First program the count mode of the timer, then program the
|
|
// interval.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(TIMER1_CONTROL_PORT,
|
|
TIMER_COMMAND_COUNTER0 |
|
|
TIMER_COMMAND_RW_16BIT |
|
|
TIMER_COMMAND_MODE2);
|
|
IO_DELAY();
|
|
|
|
WRITE_PORT_USHORT_PAIR(TIMER1_DATA_PORT0,
|
|
TIMER1_DATA_PORT0,
|
|
PERFORMANCE_INTERVAL);
|
|
IO_DELAY();
|
|
|
|
HalpUse8254 |= PERF_8254_INITIALIZED;
|
|
HalpReleaseSystemHardwareSpinLock();
|
|
}
|
|
|
|
HalpRestoreInterrupts(flags);
|
|
}
|
|
|
|
BOOLEAN
|
|
HalpClockInterruptStub (
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID ServiceContext,
|
|
IN PKTRAP_FRAME TrapFrame
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is entered as the result of an interrupt generated by
|
|
CLOCK2. Its function is to dismiss the interrupt and return.
|
|
|
|
This routine is executed on P0 during phase 0.
|
|
|
|
Arguments:
|
|
|
|
Interrupt - Supplies a pointer to the kernel interrupt object
|
|
|
|
ServiceContext - Supplies the service context
|
|
|
|
TrapFrame - Supplise a pointer to the trap frame
|
|
|
|
Return Value:
|
|
|
|
TRUE
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR status;
|
|
|
|
UNREFERENCED_PARAMETER(Interrupt);
|
|
UNREFERENCED_PARAMETER(ServiceContext);
|
|
UNREFERENCED_PARAMETER(TrapFrame);
|
|
|
|
//
|
|
// Clear the interrupt flag on the RTC by banging on the CMOS. On some
|
|
// systems this doesn't work the first time we do it, so we do it twice.
|
|
// It is rumored that some machines require more than this, but that
|
|
// hasn't been observed with NT.
|
|
//
|
|
|
|
CMOS_READ(CMOS_STATUS_C);
|
|
|
|
do {
|
|
status = CMOS_READ(CMOS_STATUS_C);
|
|
} while ((status & 0x80) != 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
HalpClockInterrupt (
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID ServiceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is entered as the result of an interrupt generated by
|
|
CLOCK2. Its function is to dismiss the interrupt, raise system Irql to
|
|
CLOCK2_LEVEL, update performance the counter and transfer control to
|
|
the standard system routine to update the system time and the execution
|
|
time of the current thread and process.
|
|
|
|
Thie routine is executed only on P0
|
|
|
|
Arguments:
|
|
|
|
Interrupt - Supplies a pointer to the kernel interrupt object
|
|
|
|
ServiceContext - Supplies the service context
|
|
|
|
Return Value:
|
|
|
|
TRUE
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG timeIncrement;
|
|
ULONG flags;
|
|
ULONG64 timeStamp;
|
|
LONG64 timeStampDelta;
|
|
ULONG rateCounter;
|
|
|
|
if (HalpUse8254 != FALSE) {
|
|
|
|
if (HalpSample8254 == 0) {
|
|
|
|
//
|
|
// Call KeQueryPerformanceCounter() so that wrap-around of 8254
|
|
// is detected and the base value for performance counters
|
|
// updated. Ignore returned value and reset HalpSample8254.
|
|
//
|
|
// WARNING - change reset value above if maximum RTC time
|
|
// increment is increased to be more than the current maximum
|
|
// value of 15.625 ms. Currently the call will be made every
|
|
// 3rd timer tick.
|
|
//
|
|
|
|
HalpSample8254 = 2;
|
|
KeQueryPerformanceCounter(0);
|
|
|
|
} else {
|
|
|
|
HalpSample8254 -= 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is the RTC interrupt, so we have to clear the flag on the RTC.
|
|
//
|
|
|
|
HalpAcquireCmosSpinLock();
|
|
|
|
//
|
|
// Clear the interrupt flag on the RTC by banging on the CMOS. On some
|
|
// systems this doesn't work the first time we do it, so we do it twice.
|
|
// It is rumored that some machines require more than this, but that
|
|
// hasn't been observed with NT.
|
|
//
|
|
|
|
CMOS_READ(CMOS_STATUS_C);
|
|
CMOS_READ(CMOS_STATUS_C);
|
|
|
|
HalpReleaseCmosSpinLock();
|
|
|
|
//
|
|
// Adjust the tick count as needed.
|
|
//
|
|
|
|
timeIncrement = HalpCurrentClockRateIn100ns;
|
|
HalpRateAdjustment += (UCHAR)HalpCurrentClockRateAdjustment;
|
|
if (HalpRateAdjustment < HalpCurrentClockRateAdjustment) {
|
|
timeIncrement--;
|
|
}
|
|
|
|
//
|
|
// With an APIC based system we will force a clock interrupt to all other
|
|
// processors. This is not really an IPI in the NT sense of the word,
|
|
// it uses the Local Apic to generate interrupts to other CPUs.
|
|
//
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
//
|
|
// See if we need to IPI anyone. This happens only at the lowest
|
|
// supported frequency (i.e. the value KeSetTimeIncrement is called
|
|
// with). We have an IPI Rate based on the current clock relative
|
|
// to the lowest clock rate.
|
|
//
|
|
|
|
rateCounter = HalpIpiRateCounter + HalpCurrentIpiRate;
|
|
HalpIpiRateCounter = rateCounter & 0xFF;
|
|
|
|
if (HalpIpiRateCounter != rateCounter &&
|
|
HalpIpiClock != 0) {
|
|
|
|
//
|
|
// Time to send an Ipi and at least one other processor is alive.
|
|
//
|
|
|
|
flags = HalpDisableInterrupts();
|
|
|
|
HalpStallWhileApicBusy();
|
|
LOCAL_APIC(LU_INT_CMD_LOW) = APIC_ICR_CLOCK;
|
|
|
|
HalpRestoreInterrupts(flags);
|
|
}
|
|
|
|
#endif // NT_UP
|
|
|
|
if (HalpTimerWatchdogEnabled != 0) {
|
|
HalpUpdateTimerWatchdog();
|
|
}
|
|
|
|
if (HalpClockWork != 0) {
|
|
HalpClockInterruptWork();
|
|
}
|
|
|
|
KeUpdateSystemTime(Interrupt->TrapFrame,timeIncrement);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
HalpClockInterruptPn (
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID ServiceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is entered as the result of an interrupt generated by
|
|
CLOCK2. Its function is to dismiss the interrupt, raise system Irql to
|
|
CLOCK2_LEVEL, update performance the counter and transfer control to
|
|
the standard system routine to update the system time and the execution
|
|
time of the current thread and process.
|
|
|
|
This routine is executed on all processors other than P0.
|
|
|
|
Arguments:
|
|
|
|
Interrupt - Supplies a pointer to the kernel interrupt object
|
|
|
|
ServiceContext - Supplies the service context
|
|
|
|
Return Value:
|
|
|
|
TRUE
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(ServiceContext);
|
|
|
|
HalpEnableInterrupts();
|
|
|
|
KeUpdateRunTime(Interrupt->TrapFrame);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
HalpClockInterruptWork (
|
|
VOID
|
|
)
|
|
{
|
|
struct RtcTimeIncStruc *timeIncStruc;
|
|
BOOLEAN changeRate;
|
|
|
|
//
|
|
// There is more clock interrupt work to do
|
|
//
|
|
|
|
if (HalpClockMcaQueueDpc != FALSE) {
|
|
|
|
//
|
|
// Queue an MCA dpc
|
|
//
|
|
|
|
HalpClockMcaQueueDpc = FALSE;
|
|
HalpMcaQueueDpc();
|
|
}
|
|
|
|
if (HalpClockSetMSRate != FALSE) {
|
|
|
|
//
|
|
// The clock frequency is being changed. See if we have changed
|
|
// rates since the last tick.
|
|
//
|
|
|
|
if (HalpPendingRate != 0) {
|
|
|
|
//
|
|
// A new rate was set during the last tick, so update
|
|
// globals.
|
|
//
|
|
// The next tick will occur at the rate which was programmed
|
|
// during the last tick. Update globals for the new rate
|
|
// which starts with the next tick.
|
|
//
|
|
// We will get here if there is a request for a rate change.
|
|
// There could have been two requests, that is why we are
|
|
// comparing the Pending with the NextRate.
|
|
//
|
|
|
|
timeIncStruc = &HalpRtcTimeIncrements[HalpPendingRate - 1];
|
|
|
|
HalpCurrentClockRateIn100ns = timeIncStruc->RateIn100ns;
|
|
HalpCurrentClockRateAdjustment = timeIncStruc->RateAdjustmentCnt;
|
|
HalpCurrentIpiRate = timeIncStruc->IpiRate;
|
|
|
|
if (HalpPendingRate != HalpNextMSRate) {
|
|
changeRate = TRUE;
|
|
} else {
|
|
changeRate = FALSE;
|
|
}
|
|
|
|
HalpPendingRate = 0;
|
|
|
|
if (changeRate != FALSE) {
|
|
|
|
//
|
|
// A new clock rate needs to be set. Setting the rate here
|
|
// will cause the tick after the next tick to be at the new
|
|
// rate.
|
|
//
|
|
// (The next tick is already in progress and will occur at
|
|
// the same rate as this tick.)
|
|
//
|
|
|
|
HalpAcquireCmosSpinLock();
|
|
|
|
HalpPendingRate = HalpNextMSRate;
|
|
|
|
timeIncStruc = &HalpRtcTimeIncrements[HalpPendingRate - 1];
|
|
HalpCurrentRTCRegisterA = timeIncStruc->RTCRegisterA;
|
|
|
|
CMOS_WRITE(CMOS_STATUS_A,(UCHAR)HalpCurrentRTCRegisterA);
|
|
|
|
if (HalpTimerWatchdogEnabled != FALSE) {
|
|
|
|
HalpWatchdogTsc = ReadTimeStampCounter();
|
|
HalpWatchdogCount = 0;
|
|
HalpWatchdogAvgCounter = COUNTER_TICKS_FOR_AVG;
|
|
}
|
|
|
|
HalpReleaseCmosSpinLock();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
HalpUpdateTimerWatchdog (
|
|
VOID
|
|
)
|
|
{
|
|
ULONG stackStart;
|
|
ULONG64 timeStamp;
|
|
ULONG64 timeStampDelta;
|
|
|
|
timeStamp = ReadTimeStampCounter();
|
|
timeStampDelta = timeStamp - HalpWatchdogTsc;
|
|
HalpWatchdogTsc = timeStamp;
|
|
|
|
if ((LONG64)timeStampDelta < 0) {
|
|
|
|
//
|
|
// Bogus (negative) timestamp count.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
if (*KdEnteredDebugger != FALSE) {
|
|
|
|
//
|
|
// Skip if we're in the debugger.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
if (HalpPendingRate != 0) {
|
|
|
|
//
|
|
// A new rate was set during the last tick, discontinue
|
|
// processing
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
if (HalpWatchdogAvgCounter != 0) {
|
|
|
|
//
|
|
// Increment the total counter, perform average when the count is
|
|
// reached.
|
|
//
|
|
|
|
HalpWatchdogCount += timeStampDelta;
|
|
HalpWatchdogAvgCounter -= 1;
|
|
|
|
if (HalpWatchdogAvgCounter == 0) {
|
|
HalpWatchdogCount /= COUNTER_TICKS_FOR_AVG;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (timeStampDelta <= HalpWatchdogCount) {
|
|
return;
|
|
}
|
|
|
|
if (HalpTimerWatchdogCurFrame != NULL &&
|
|
HalpTimerWatchdogStorageOverflow == FALSE) {
|
|
|
|
PVOID pSrc;
|
|
ULONG copyBytes;
|
|
|
|
//
|
|
// Copy FRAME_COPY_SIZE dwords from the stack, or to next
|
|
// page boundary, whichever is less.
|
|
//
|
|
|
|
pSrc = &stackStart;
|
|
copyBytes = (ULONG)(PAGE_SIZE - ((ULONG_PTR)pSrc & (PAGE_SIZE-1)));
|
|
if (copyBytes > FRAME_COPY_SIZE) {
|
|
copyBytes = FRAME_COPY_SIZE;
|
|
}
|
|
|
|
RtlCopyMemory(HalpTimerWatchdogCurFrame, pSrc, copyBytes);
|
|
(ULONG_PTR)HalpTimerWatchdogCurFrame += copyBytes;
|
|
|
|
//
|
|
// If we didn't copy an entire FRAME_COPY_SIZE buffer, zero
|
|
// fill.
|
|
//
|
|
|
|
copyBytes = FRAME_COPY_SIZE - copyBytes;
|
|
if (copyBytes > 0) {
|
|
RtlZeroMemory(HalpTimerWatchdogCurFrame,copyBytes);
|
|
(ULONG_PTR)HalpTimerWatchdogCurFrame += copyBytes;
|
|
}
|
|
|
|
if (HalpTimerWatchdogCurFrame >= HalpTimerWatchdogLastFrame) {
|
|
HalpTimerWatchdogStorageOverflow = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // _WIN64
|