windows-nt/Source/XPSP1/NT/base/hals/halacpi/amd64/mpprofil.c

507 lines
9.2 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
mpprofil.c
Abstract:
This module implements the code necessary to initialize, field and
process the profile interrupt.
Author:
Shie-Lin Tzong (shielint) 12-Jan-1990
Environment:
Kernel mode only.
Revision History:
bryanwi 20-Sep-90
Forrest Foltz (forrestf) 28-Oct-2000
Ported from ixprofil.asm to ixprofil.c
--*/
#include "halcmn.h"
#define APIC_TIMER_ENABLED (PERIODIC_TIMER | APIC_PROFILE_VECTOR)
#define APIC_TIMER_DISABLED (INTERRUPT_MASKED | \
PERIODIC_TIMER | \
APIC_PROFILE_VECTOR)
#define TIMER_ROUND(x) ((((x) + 10000 / 2) / 10000) * 10000)
ULONG HalpProfileRunning;
VOID (*HalpPerfInterruptHandler)(PKTRAP_FRAME);
VOID
HalStartProfileInterrupt(
IN ULONG Reserved
)
/*++
Routine Description:
What we do here is set the interrupt rate to the value that's been set
by the KeSetProfileInterval routine. Then enable the APIC Timer interrupt.
This function gets called on every processor so the hal can enable
a profile interrupt on each processor.
Arguments:
Reserved - Not used, must be zero.
Return Value:
None.
--*/
{
ULONG initialCount;
ASSERT(Reserved == 0);
initialCount = HalpGetCurrentHalPcr()->ProfileCountDown;
LOCAL_APIC(LU_INITIAL_COUNT) = initialCount;
HalpProfileRunning = TRUE;
//
// Set the Local APIC Timer to interrupt periodically at
// APIC_PROFILE_VECTOR
//
LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_ENABLED;
}
VOID
HalStopProfileInterrupt (
IN ULONG Reserved
)
/*++
Routine Description:
Stop the profile interrupt that was started with
HalpStartProfileInterrupt;
This function gets called on every processor so the hal can disable
a profile interrupt on each processor.
Arguments:
Reserved - Not used, must be zero.
Return Value:
None.
--*/
{
ASSERT(Reserved == 0);
HalpProfileRunning = FALSE;
LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_DISABLED;
}
ULONG_PTR
HalSetProfileInterval (
ULONG_PTR Interval
)
/*++
Routine Description:
This procedure sets the interrupt rate (and thus the sampling
interval) for the profiling interrupt.
Arguments:
Interval - Supplies the desired profile interrupt interval in 100ns
units (MINIMUM is 1221 or 122.1 uS) see ke\profobj.c
Return Value:
Interval actually used
--*/
{
ULONG64 period;
ULONG apicFreqency;
PHAL_PCR halPcr;
ULONG countDown;
halPcr = HalpGetCurrentHalPcr();
//
// Limit the interrupt period to 1 second
//
if (Interval > TIME_UNITS_PER_SECOND) {
period = TIME_UNITS_PER_SECOND;
} else {
period = Interval;
}
//
// Compute the countdown value corresponding to the desired period.
// The calculation is done with 64-bit intermediate values.
//
countDown =
(ULONG)(period * halPcr->ApicClockFreqHz) / TIME_UNITS_PER_SECOND;
halPcr->ProfileCountDown = countDown;
LOCAL_APIC(LU_INITIAL_COUNT) = countDown;
return period;
}
VOID
#if defined(MMTIMER)
HalpAcpiTimerCalibratePerfCount (
#else
HalCalibratePerformanceCounter (
#endif
IN LONG volatile *Number,
IN ULONGLONG NewCount
)
/*++
Routine Description:
This routine calibrates the performance counter value for a
multiprocessor system. The calibration can be done by zeroing
the current performance counter, or by calculating a per-processor
skewing between each processors counter.
Arguments:
Number - Supplies a pointer to count of the number of processors in
the configuration.
NewCount - Supplies the value to synchronize the counter too
Return Value:
None.
--*/
{
ULONG flags;
PHAL_PCR halPcr;
flags = HalpDisableInterrupts();
//
// Store the count in the PCR
//
halPcr = HalpGetCurrentHalPcr();
halPcr->PerfCounter = NewCount;
//
// Wait here until all processors arrive
//
InterlockedDecrement(Number);
while (*Number > 0) {
;
}
PROCESSOR_FENCE;
//
// Zero the time stamp counter, restore interrupts and return.
//
WriteMSR(MSR_TSC,0);
HalpRestoreInterrupts(flags);
}
VOID
HalpWaitForCmosRisingEdge (
VOID
)
/*++
Routine Description:
Waits until the rising edge of the CMOS_STATUS_BUSY bit is detected.
Note - The CMOS spin lock must be acquired before calling this routine.
Arguments:
None.
Return Value:
None.
--*/
{
UCHAR value;
//
// The simulator takes about a day to go through this, so for right
//
#if !defined(_AMD64_SIMULATOR_)
//
// We're going to be polling the CMOS_STATUS_A register. Program
// that register address here, outside of the loop.
//
WRITE_PORT_UCHAR(CMOS_ADDRESS_PORT,CMOS_STATUS_A);
//
// Wait for the status bit to be clear
//
do {
value = READ_PORT_UCHAR(CMOS_DATA_PORT);
} while ((value & CMOS_STATUS_BUSY) != 0);
//
// Now wait for the rising edge of the status bit
//
do {
value = READ_PORT_UCHAR(CMOS_DATA_PORT);
} while ((value & CMOS_STATUS_BUSY) == 0);
#endif
}
ULONG
HalpScaleTimers (
VOID
)
/*++
Routine Description:
Determines the frequency of the APIC timer. This routine is run
during initialization
Arguments:
None.
Return Value:
None.
--*/
{
ULONG flags;
ULONG passCount;
ULONG64 cpuFreq;
ULONG apicFreq;
UCHAR value;
PHAL_PCR halPcr;
HalpAcquireCmosSpinLock();
flags = HalpDisableInterrupts();
LOCAL_APIC(LU_TIMER_VECTOR) = APIC_TIMER_DISABLED;
LOCAL_APIC(LU_DIVIDER_CONFIG) = LU_DIVIDE_BY_1;
passCount = 2;
while (passCount > 0) {
//
// Make sure the write has occured
//
LOCAL_APIC(LU_TIMER_VECTOR);
//
// Wait for the rising edge of the UIP bit, this is the start of the
// cycle.
//
HalpWaitForCmosRisingEdge();
//
// At this point the UIP bit has just changed to the set state.
// Clear the time stamp counter and start the APIC counting down
// from it's maximum value.
//
PROCESSOR_FENCE;
LOCAL_APIC(LU_INITIAL_COUNT) = 0xFFFFFFFF;
cpuFreq = ReadTimeStampCounter();
//
// Wait for the next rising edge, this marks the end of the CMOS
// clock update cycle.
//
HalpWaitForCmosRisingEdge();
PROCESSOR_FENCE;
apicFreq = 0xFFFFFFFF - LOCAL_APIC(LU_CURRENT_COUNT);
cpuFreq = ReadTimeStampCounter() - cpuFreq;
passCount -= 1;
}
halPcr = HalpGetCurrentHalPcr();
//
// cpuFreq is elapsed timestamp in one second. Round to nearest
// 10Khz and store.
//
halPcr->TSCHz = TIMER_ROUND(cpuFreq);
//
// Calculate the apic frequency, rounding to the nearest 10Khz
//
apicFreq = TIMER_ROUND(apicFreq);
halPcr->ApicClockFreqHz = apicFreq;
//
// Store microsecond representation of TSC frequency.
//
halPcr->StallScaleFactor = (ULONG)(halPcr->TSCHz / 1000000);
if ((halPcr->TSCHz % 1000000) != 0) {
halPcr->StallScaleFactor += 1;
}
HalpReleaseCmosSpinLock();
halPcr->ProfileCountDown = apicFreq;
//
// Set the interrupt rate in the chip and return the apic frequency
//
LOCAL_APIC(LU_INITIAL_COUNT) = apicFreq;
HalpRestoreInterrupts(flags);
return halPcr->ApicClockFreqHz;
}
BOOLEAN
HalpProfileInterrupt (
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext
)
/*++
Routine Description:
This routine is entered as the result of a profile interrupt. Its
function is to dismiss the interrupt, raise system Irql to
HAL_PROFILE_LEVEL and transfer control to the standard system routine
to process any active profiles.
Arguments:
Interrupt - Supplies a pointer to the kernel interrupt object
ServiceContext - Supplies the service context
Return Value:
TRUE
--*/
{
UNREFERENCED_PARAMETER(ServiceContext);
if (HalpProfileRunning != FALSE) {
//
// BUGBUG what do we use for source?
//
KeProfileInterruptWithSource(Interrupt->TrapFrame,0);
}
return TRUE;
}
BOOLEAN
HalpPerfInterrupt (
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext
)
/*++
Routine Description:
This routine is entered as the result of a perf interrupt. Its
function is to dismiss the interrupt, raise system Irql to
HAL_PROFILE_LEVEL and transfer control to the standard system routine
to process any active profiles.
Arguments:
Interrupt - Supplies a pointer to the kernel interrupt object
ServiceContext - Supplies the service context
Return Value:
TRUE
--*/
{
UNREFERENCED_PARAMETER(Interrupt);
if (HalpPerfInterruptHandler != NULL) {
HalpPerfInterruptHandler(Interrupt->TrapFrame);
} else {
ASSERT(FALSE);
}
//
// Starting with the Willamette processor, the perf interrupt gets masked
// on interrupting. Need to clear the mask before leaving the interrupt
// handler.
//
LOCAL_APIC(LU_PERF_VECTOR), ~INTERRUPT_MASKED;
return TRUE;
}