592 lines
14 KiB
C
592 lines
14 KiB
C
/*++
|
||
|
||
Copyright (c) 1994 Digital Equipment Corporation
|
||
|
||
Module Name:
|
||
|
||
ev4prof.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Profile Counter using the performance
|
||
counters within the EV4 core. This module is appropriate for all
|
||
machines based on microprocessors using the EV4 core.
|
||
|
||
N.B. - This module assumes that all processors in a multiprocessor
|
||
system are running the microprocessor at the same clock speed.
|
||
|
||
Author:
|
||
|
||
Joe Notarangelo 22-Feb-1994
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "axp21064.h"
|
||
|
||
|
||
//
|
||
// Define space in the HAL-reserved part of the PCR structure for each
|
||
// performance counter's interval count
|
||
//
|
||
// Note that ev4ints.s depends on these positions in the PCR.
|
||
//
|
||
#define PCRProfileCount ((PULONG)(HAL_PCR->ProfileCount.ProfileCount))
|
||
#define PCRProfileCountReload ((PULONG)(&HAL_PCR->ProfileCount.ProfileCountReload))
|
||
|
||
|
||
//
|
||
// Define the currently selected profile source for each counter
|
||
//
|
||
KPROFILE_SOURCE HalpProfileSource0;
|
||
KPROFILE_SOURCE HalpProfileSource1;
|
||
|
||
#define INTERVAL_DELTA (10)
|
||
|
||
//
|
||
// Define the mapping between possible profile sources and the
|
||
// CPU-specific settings.
|
||
//
|
||
typedef struct _HALP_PROFILE_MAPPING {
|
||
BOOLEAN Supported;
|
||
ULONG MuxControl;
|
||
ULONG Counter;
|
||
ULONG EventCount;
|
||
ULONG NumberOfTicks;
|
||
} HALP_PROFILE_MAPPING, *PHALP_PROFILE_MAPPING;
|
||
|
||
HALP_PROFILE_MAPPING HalpProfileMapping[ProfileMaximum] =
|
||
{
|
||
{TRUE, Ev4TotalCycles, Ev4PerformanceCounter0, Ev4CountEvents2xx12, 10},
|
||
{FALSE, 0, 0, 0, 0},
|
||
{TRUE, Ev4TotalIssues, Ev4PerformanceCounter0, Ev4CountEvents2xx16, 10},
|
||
{TRUE, Ev4PipelineDry, Ev4PerformanceCounter0, Ev4CountEvents2xx16, 10},
|
||
{TRUE, Ev4LoadInstruction, Ev4PerformanceCounter0, Ev4CountEvents2xx12, 10},
|
||
{TRUE, Ev4PipelineFrozen, Ev4PerformanceCounter0, Ev4CountEvents2xx16, 10},
|
||
{TRUE, Ev4BranchInstructions, Ev4PerformanceCounter0, Ev4CountEvents2xx12, 10},
|
||
{TRUE, Ev4TotalNonIssues, Ev4PerformanceCounter0, Ev4CountEvents2xx16, 10},
|
||
{TRUE, Ev4DcacheMiss, Ev4PerformanceCounter1, Ev4CountEvents2xx12, 10},
|
||
{TRUE, Ev4IcacheMiss, Ev4PerformanceCounter1, Ev4CountEvents2xx12, 10},
|
||
{FALSE, 0, 0, 0, 0},
|
||
{TRUE, Ev4BranchMispredicts, Ev4PerformanceCounter1, Ev4CountEvents2xx12, 10},
|
||
{TRUE, Ev4StoreInstructions, Ev4PerformanceCounter1, Ev4CountEvents2xx12, 10},
|
||
{TRUE, Ev4FPInstructions, Ev4PerformanceCounter1, Ev4CountEvents2xx12, 10},
|
||
{TRUE, Ev4IntegerOperate, Ev4PerformanceCounter1, Ev4CountEvents2xx12, 10},
|
||
{TRUE, Ev4DualIssues, Ev4PerformanceCounter1, Ev4CountEvents2xx12, 10},
|
||
{FALSE, 0, 0, 0, 0},
|
||
{FALSE, 0, 0, 0, 0},
|
||
{TRUE, Ev4PalMode, Ev4PerformanceCounter0, Ev4CountEvents2xx16, 10},
|
||
{TRUE, Ev4TotalCycles, Ev4PerformanceCounter0, Ev4CountEvents2xx16, 10},
|
||
{FALSE, 0, 0, 0, 0},
|
||
{FALSE, 0, 0, 0, 0}
|
||
};
|
||
|
||
BOOLEAN
|
||
HalQueryProfileInterval(
|
||
IN KPROFILE_SOURCE Source
|
||
);
|
||
|
||
NTSTATUS
|
||
HalSetProfileSourceInterval(
|
||
IN KPROFILE_SOURCE ProfileSource,
|
||
IN OUT ULONG *Interval
|
||
);
|
||
|
||
|
||
|
||
NTSTATUS
|
||
HalpProfileSourceInformation (
|
||
OUT PVOID Buffer,
|
||
IN ULONG BufferLength,
|
||
OUT PULONG ReturnedLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the HAL_PROFILE_SOURCE_INFORMATION for this processor.
|
||
|
||
Arguments:
|
||
|
||
Buffer - output buffer
|
||
BufferLength - length of buffer on input
|
||
ReturnedLength - The length of data returned
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_BUFFER_TOO_SMALL - The ReturnedLength contains the buffersize
|
||
currently needed.
|
||
|
||
--*/
|
||
{
|
||
PHAL_PROFILE_SOURCE_INFORMATION SourceInfo;
|
||
NTSTATUS Status;
|
||
|
||
|
||
if (BufferLength != sizeof(HAL_PROFILE_SOURCE_INFORMATION)) {
|
||
Status = STATUS_INFO_LENGTH_MISMATCH;
|
||
return Status;
|
||
}
|
||
|
||
SourceInfo = (PHAL_PROFILE_SOURCE_INFORMATION)Buffer;
|
||
SourceInfo->Supported = HalQueryProfileInterval(SourceInfo->Source);
|
||
|
||
if (SourceInfo->Supported) {
|
||
SourceInfo->Interval =
|
||
HalpProfileMapping[SourceInfo->Source].EventCount *
|
||
HalpProfileMapping[SourceInfo->Source].NumberOfTicks;
|
||
if (SourceInfo->Source == ProfileTotalIssues) {
|
||
//
|
||
// Convert total issues/2 back into total issues
|
||
//
|
||
SourceInfo->Interval = SourceInfo->Interval * 2;
|
||
}
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpProfileSourceInterval (
|
||
OUT PVOID Buffer,
|
||
IN ULONG BufferLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the HAL_PROFILE_SOURCE_INTERVAL for this processor.
|
||
|
||
Arguments:
|
||
|
||
Buffer - output buffer
|
||
BufferLength - length of buffer on input
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_BUFFER_TOO_SMALL - The ReturnedLength contains the buffersize
|
||
currently needed.
|
||
|
||
--*/
|
||
{
|
||
PHAL_PROFILE_SOURCE_INTERVAL Interval;
|
||
NTSTATUS Status;
|
||
|
||
|
||
if (BufferLength != sizeof(HAL_PROFILE_SOURCE_INTERVAL)) {
|
||
Status = STATUS_INFO_LENGTH_MISMATCH;
|
||
return Status;
|
||
}
|
||
|
||
Interval = (PHAL_PROFILE_SOURCE_INTERVAL)Buffer;
|
||
Status = HalSetProfileSourceInterval(Interval->Source,
|
||
&Interval->Interval);
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
HalpInitializeProfiler(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called during initialization to initialize profiling
|
||
for each processor in the system.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PKINTERRUPT InterruptObject;
|
||
KIRQL Irql;
|
||
PKPRCB Prcb = PCR->Prcb;
|
||
ULONG Vector;
|
||
|
||
//
|
||
// Establish the profile interrupt as the interrupt handler for
|
||
// all performance counter interrupts.
|
||
//
|
||
|
||
PCR->InterruptRoutine[PC0_VECTOR] = HalpPerformanceCounter0Interrupt;
|
||
PCR->InterruptRoutine[PC1_VECTOR] = HalpPerformanceCounter1Interrupt;
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
HalQueryProfileInterval(
|
||
IN KPROFILE_SOURCE Source
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a profile source, returns whether or not that source is
|
||
supported.
|
||
|
||
Arguments:
|
||
|
||
Source - Supplies the profile source
|
||
|
||
Return Value:
|
||
|
||
TRUE - Profile source is supported
|
||
|
||
FALSE - Profile source is not supported
|
||
|
||
--*/
|
||
|
||
{
|
||
if (Source > (sizeof(HalpProfileMapping)/sizeof(HALP_PROFILE_MAPPING))) {
|
||
return(FALSE);
|
||
}
|
||
|
||
return(HalpProfileMapping[Source].Supported);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
HalSetProfileSourceInterval(
|
||
IN KPROFILE_SOURCE ProfileSource,
|
||
IN OUT ULONG *Interval
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the profile interval for a specified profile source
|
||
|
||
Arguments:
|
||
|
||
ProfileSource - Supplies the profile source
|
||
|
||
Interval - Supplies the specified profile interval
|
||
Returns the actual profile interval
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG FastTickPeriod;
|
||
ULONG SlowTickPeriod;
|
||
ULONG TickPeriod;
|
||
ULONGLONG CountEvents;
|
||
ULONG FastCountEvents;
|
||
ULONG SlowCountEvents;
|
||
ULONGLONG TempInterval;
|
||
|
||
if (!HalQueryProfileInterval(ProfileSource)) {
|
||
return(STATUS_NOT_IMPLEMENTED);
|
||
}
|
||
|
||
if (ProfileSource == ProfileTime) {
|
||
|
||
//
|
||
// Convert the clock tick period (in 100ns units ) into
|
||
// a cycle count period
|
||
//
|
||
|
||
CountEvents = ((ULONGLONG)(*Interval) * 100000) / PCR->CycleClockPeriod;
|
||
} else if (ProfileSource == ProfileTotalIssues) {
|
||
|
||
//
|
||
// Convert the total issue events into the wonky
|
||
// total issues/2 form implemented by EV4.
|
||
//
|
||
|
||
CountEvents = (ULONGLONG)(*Interval / 2);
|
||
} else {
|
||
CountEvents = (ULONGLONG)*Interval;
|
||
}
|
||
|
||
if (HalpProfileMapping[ProfileSource].Counter == Ev4PerformanceCounter1) {
|
||
FastCountEvents = Ev4CountEvents2xx8;
|
||
SlowCountEvents = Ev4CountEvents2xx12;
|
||
} else {
|
||
FastCountEvents = Ev4CountEvents2xx12;
|
||
SlowCountEvents = Ev4CountEvents2xx16;
|
||
}
|
||
|
||
//
|
||
// Limit the interval to the smallest interval we can time.
|
||
//
|
||
if (CountEvents < FastCountEvents) {
|
||
CountEvents = (ULONGLONG)FastCountEvents;
|
||
}
|
||
|
||
//
|
||
// Assume we will use the fast event count
|
||
//
|
||
HalpProfileMapping[ProfileSource].EventCount = FastCountEvents;
|
||
HalpProfileMapping[ProfileSource].NumberOfTicks =
|
||
(ULONG)((CountEvents + FastCountEvents - 1) / FastCountEvents);
|
||
|
||
//
|
||
// See if we can successfully use the slower period. If the requested
|
||
// interval is greater than the slower tick period and the difference
|
||
// between the requested interval and the interval that we can deliver
|
||
// with the slower clock is acceptable, then use the slower clock.
|
||
// We define an acceptable difference as a difference of less than
|
||
// INTERVAL_DELTA of the requested interval.
|
||
//
|
||
if (CountEvents > SlowCountEvents) {
|
||
ULONG NewInterval;
|
||
|
||
NewInterval = (ULONG)(((CountEvents + SlowCountEvents-1) /
|
||
SlowCountEvents) * SlowCountEvents);
|
||
if (((NewInterval - CountEvents) * 100 / CountEvents) < INTERVAL_DELTA) {
|
||
HalpProfileMapping[ProfileSource].EventCount = SlowCountEvents;
|
||
HalpProfileMapping[ProfileSource].NumberOfTicks = NewInterval / SlowCountEvents;
|
||
}
|
||
}
|
||
|
||
*Interval = HalpProfileMapping[ProfileSource].EventCount *
|
||
HalpProfileMapping[ProfileSource].NumberOfTicks;
|
||
|
||
if (ProfileSource == ProfileTime) {
|
||
//
|
||
// Convert cycle count back into 100ns clock ticks
|
||
//
|
||
// Use 64-bit integer to prevent overflow.
|
||
//
|
||
TempInterval = (ULONGLONG)(*Interval) * (ULONGLONG)(PCR->CycleClockPeriod);
|
||
*Interval = (ULONG)(TempInterval / 100000);
|
||
} else if (ProfileSource == ProfileTotalIssues) {
|
||
//
|
||
// Convert issues/2 count back into issues
|
||
//
|
||
TempInterval = (ULONGLONG)(*Interval) * 2;
|
||
*Interval = (ULONG)TempInterval;
|
||
}
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
ULONG
|
||
HalSetProfileInterval (
|
||
IN ULONG Interval
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the profile interrupt interval.
|
||
|
||
Arguments:
|
||
|
||
Interval - Supplies the desired profile interval in 100ns units.
|
||
|
||
Return Value:
|
||
|
||
The actual profile interval.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG NewInterval;
|
||
|
||
NewInterval = Interval;
|
||
HalSetProfileSourceInterval(ProfileTime, &NewInterval);
|
||
return(NewInterval);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
HalStartProfileInterrupt (
|
||
KPROFILE_SOURCE ProfileSource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine turns on the profile interrupt.
|
||
|
||
N.B. This routine must be called at PROCLK_LEVEL while holding the
|
||
profile lock.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PerformanceCounter;
|
||
ULONG MuxControl;
|
||
ULONG EventCount;
|
||
|
||
//
|
||
// Check input to see if we are turning on a source that is
|
||
// supported. If it is unsupported, just return.
|
||
//
|
||
|
||
if ((ProfileSource > (sizeof(HalpProfileMapping)/sizeof(HALP_PROFILE_MAPPING))) ||
|
||
(!HalpProfileMapping[ProfileSource].Supported)) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Set the performance counter within the processor to begin
|
||
// counting total cycles.
|
||
//
|
||
PerformanceCounter = HalpProfileMapping[ProfileSource].Counter;
|
||
MuxControl = HalpProfileMapping[ProfileSource].MuxControl;
|
||
|
||
if (PerformanceCounter == Ev4PerformanceCounter0) {
|
||
|
||
HalpProfileSource0 = ProfileSource;
|
||
EventCount = (HalpProfileMapping[ProfileSource].EventCount == Ev4CountEvents2xx12) ?
|
||
Ev4EventCountHigh :
|
||
Ev4EventCountLow;
|
||
HalpWritePerformanceCounter( PerformanceCounter,
|
||
TRUE,
|
||
MuxControl,
|
||
EventCount );
|
||
|
||
PCRProfileCountReload[0] = HalpProfileMapping[ProfileSource].NumberOfTicks;
|
||
PCRProfileCount[0] = HalpProfileMapping[ProfileSource].NumberOfTicks;
|
||
|
||
//
|
||
// Enable the performance counter interrupt.
|
||
//
|
||
|
||
HalEnableSystemInterrupt ( PC0_VECTOR,
|
||
PROFILE_LEVEL,
|
||
LevelSensitive );
|
||
|
||
|
||
} else {
|
||
|
||
HalpProfileSource1 = ProfileSource;
|
||
EventCount = (HalpProfileMapping[ProfileSource].EventCount == Ev4CountEvents2xx12) ?
|
||
Ev4EventCountLow :
|
||
Ev4EventCountHigh;
|
||
HalpWritePerformanceCounter( PerformanceCounter,
|
||
TRUE,
|
||
MuxControl,
|
||
EventCount );
|
||
|
||
PCRProfileCountReload[1] = HalpProfileMapping[ProfileSource].NumberOfTicks;
|
||
PCRProfileCount[1] = HalpProfileMapping[ProfileSource].NumberOfTicks;
|
||
|
||
//
|
||
// Enable the performance counter interrupt.
|
||
//
|
||
|
||
HalEnableSystemInterrupt ( PC1_VECTOR,
|
||
PROFILE_LEVEL,
|
||
LevelSensitive );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
HalStopProfileInterrupt (
|
||
KPROFILE_SOURCE ProfileSource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine turns off the profile interrupt.
|
||
|
||
N.B. This routine must be called at PROCLK_LEVEL while holding the
|
||
profile lock.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PerformanceCounter;
|
||
ULONG Vector;
|
||
|
||
//
|
||
// Check input to see if we are turning off a source that is
|
||
// supported. If it is unsupported, just return.
|
||
//
|
||
|
||
if ((ProfileSource > (sizeof(HalpProfileMapping)/sizeof(HALP_PROFILE_MAPPING))) ||
|
||
(!HalpProfileMapping[ProfileSource].Supported)) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Stop the performance counter from interrupting.
|
||
//
|
||
|
||
PerformanceCounter = HalpProfileMapping[ProfileSource].Counter;
|
||
HalpWritePerformanceCounter( PerformanceCounter,
|
||
FALSE,
|
||
0,
|
||
0 );
|
||
|
||
//
|
||
// Disable the performance counter interrupt.
|
||
//
|
||
if (PerformanceCounter == Ev4PerformanceCounter0) {
|
||
HalDisableSystemInterrupt( PC0_VECTOR, PROFILE_LEVEL );
|
||
|
||
//
|
||
// Clear the current profile count. Can't clear value in PCR
|
||
// since a profile interrupt could be pending or in progress
|
||
// so clear the reload counter.
|
||
//
|
||
|
||
PCRProfileCountReload[0] = 0;
|
||
} else {
|
||
HalDisableSystemInterrupt( PC1_VECTOR, PROFILE_LEVEL );
|
||
|
||
//
|
||
// Clear the current profile count. Can't clear value in PCR
|
||
// since a profile interrupt could be pending or in progress
|
||
// so clear the reload counter.
|
||
//
|
||
|
||
PCRProfileCountReload[0] = 0;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|