Leaked source code of windows server 2003
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.
 
 
 
 
 
 

689 lines
16 KiB

/*++
Copyright (c) 2002 Microsoft Corporation
Module Name:
Amd64.c
Abstract:
This module implements profiling functions for the Amd64 platform.
Author:
Steve Deng (sdeng) 18-Jun-2002
Environment:
Kernel mode only.
--*/
#include "halcmn.h"
#include "mpprofil.h"
#include "amd64.h"
//
// Local prototypes
//
VOID
Amd64InitializeProfiling(
VOID
);
NTSTATUS
Amd64EnableMonitoring(
KPROFILE_SOURCE ProfileSource
);
VOID
Amd64DisableMonitoring(
KPROFILE_SOURCE ProfileSource
);
NTSTATUS
Amd64SetInterval(
IN KPROFILE_SOURCE ProfileSource,
IN OUT ULONG_PTR *Interval
);
NTSTATUS
Amd64QueryInformation(
IN HAL_QUERY_INFORMATION_CLASS InformationType,
IN ULONG BufferSize,
IN OUT PVOID Buffer,
OUT PULONG ReturnedLength
);
VOID
Amd64CheckOverflowStatus(
POVERFLOW_STATUS pOverflowStatus
);
NTSTATUS
HalpGetProfileDescriptor(
KPROFILE_SOURCE ProfileSource,
PAMD64_PROFILE_SOURCE_DESCRIPTOR *ProfileSourceDescriptor
);
NTSTATUS
HalpAllocateCounter(
KPROFILE_SOURCE ProfileSource,
OUT PULONG Counter
);
VOID
HalpFreeCounter(
ULONG Counter
);
#pragma alloc_text(PAGE, Amd64QueryInformation)
#pragma alloc_text(INIT, Amd64InitializeProfiling)
PROFILE_INTERFACE Amd64PriofileInterface = {
Amd64InitializeProfiling,
Amd64EnableMonitoring,
Amd64DisableMonitoring,
Amd64SetInterval,
Amd64QueryInformation,
Amd64CheckOverflowStatus
};
//
// The status of counter registers
//
typedef struct _COUNTER_STATUS {
BOOLEAN Idle;
KPROFILE_SOURCE ProfileSource;
} COUNTER_STATUS;
COUNTER_STATUS
CounterStatus[MAXIMUM_PROCESSORS][AMD64_NUMBER_COUNTERS];
//
// The profile sources of overflowed counters
//
KPROFILE_SOURCE
OverflowedProfileList[MAXIMUM_PROCESSORS][AMD64_NUMBER_COUNTERS];
VOID
Amd64InitializeProfiling(
VOID
)
/*++
Routine Description:
This function does one time initialization of the performance monitoring
registers and related data structures.
Arguments:
None.
Return Value:
None.
--*/
{
LONG i;
//
// Initialize all PerfEvtSel registers to zero. This will effectively
// disable all counters.
//
for (i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
WRMSR(MSR_PERF_EVT_SEL0 + i, 0);
HalpFreeCounter(i);
}
}
NTSTATUS
Amd64EnableMonitoring(
KPROFILE_SOURCE ProfileSource
)
/*++
Routine Description:
This function enables the monitoring of the hardware events specified
by ProfileSource and set up MSRs to generate performance monitor
interrupt (PMI) when counters overflow.
Arguments:
ProfileSource - Supplies the Profile Source
Return Value:
STATUS_SUCCESS - If the monitoring of the event is successfully enabled.
STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
STATUS_DEVICE_BUSY - If no free counters available.
--*/
{
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
NTSTATUS Status;
ULONG i;
//
// If the specified ProfileSource is not supported, return immediately
//
Status = HalpGetProfileDescriptor(ProfileSource,
&ProfileSourceDescriptor);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Get an idle counter if available. Otherwise return immediately.
//
Status = HalpAllocateCounter(ProfileSource, &i);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Set counter register to its initial value
//
WRMSR (MSR_PERF_CTR0 + i, 0 - ProfileSourceDescriptor->Interval);
//
// Enable counting and overflow interrupt
//
WRMSR (MSR_PERF_EVT_SEL0 + i,
ProfileSourceDescriptor->PerfEvtSelDef |
PERF_EVT_SEL_INTERRUPT |
PERF_EVT_SEL_ENABLE);
return STATUS_SUCCESS;
}
VOID
Amd64DisableMonitoring(
KPROFILE_SOURCE ProfileSource
)
/*++
Routine Description:
This function stops the monitoring of the hardware event specified
by ProfileSource, and disables the interrupt associated with the event.
Arguments:
ProfileSource - Supplies the Profile Source
Return Value:
None.
--*/
{
NTSTATUS Status;
ULONG ProcessorNumber, i;
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
//
// If the specified ProfileSource is not supported, return immediately.
//
Status = HalpGetProfileDescriptor(ProfileSource, &ProfileSourceDescriptor);
if (!NT_SUCCESS(Status)) {
return;
}
ProcessorNumber = KeGetCurrentProcessorNumber();
for (i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
//
// Find out the counter assigned to the given profile source and
// disable it
//
if (!(CounterStatus[ProcessorNumber][i].Idle) &&
(ProfileSource == CounterStatus[ProcessorNumber][i].ProfileSource)){
//
// Disable counting and overflow interrupt
//
WRMSR( MSR_PERF_EVT_SEL0 + i,
ProfileSourceDescriptor->PerfEvtSelDef &
~(PERF_EVT_SEL_INTERRUPT | PERF_EVT_SEL_ENABLE));
//
// Free up the counter
//
HalpFreeCounter (i);
break;
}
}
return;
}
NTSTATUS
Amd64SetInterval(
IN KPROFILE_SOURCE ProfileSource,
IN OUT ULONG_PTR *Interval
)
/*++
Routine Description:
This function sets the interrupt interval of given profile source
for Amd64 platform.
It is assumed that all processors in the system use same interval
value for same ProfileSource.
Arguments:
ProfileSource - Supplies the profile source.
Interval - Supplies the interval value and returns the actual interval.
Return Value:
STATUS_SUCCESS - If the profile interval is successfully updated.
STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
--*/
{
NTSTATUS Status;
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
Status = HalpGetProfileDescriptor(ProfileSource, &ProfileSourceDescriptor);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (*Interval < ProfileSourceDescriptor->MinInterval) {
*Interval = ProfileSourceDescriptor->MinInterval;
}
if (*Interval > ProfileSourceDescriptor->MaxInterval) {
*Interval = ProfileSourceDescriptor->MaxInterval;
}
ProfileSourceDescriptor->Interval = *Interval;
return STATUS_SUCCESS;
}
VOID
Amd64CheckOverflowStatus(
POVERFLOW_STATUS pOverflowStatus
)
/*++
Routine Description:
This function find out the overflowed counters and return the related
profile sources to the caller.
Arguments:
ProfileSource - Supplies the profile source.
Return Value:
None.
--*/
{
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
KPROFILE_SOURCE ProfileSource;
ULONG64 CurrentCount, Mask;
NTSTATUS Status;
LONG i, j;
ULONG ProcessorNumber;
ProcessorNumber = KeGetCurrentProcessorNumber();
for(i = j = 0; i < AMD64_NUMBER_COUNTERS; i++) {
if (!(CounterStatus[ProcessorNumber][i].Idle)) {
ProfileSource = CounterStatus[ProcessorNumber][i].ProfileSource;
Status = HalpGetProfileDescriptor (ProfileSource,
&ProfileSourceDescriptor);
if (NT_SUCCESS(Status)) {
//
// Mask off the reserved bits
//
Mask = (((ULONG64)1 << AMD64_COUNTER_RESOLUTION) - 1);
CurrentCount = RDMSR(MSR_PERF_CTR0 + i);
//
// An overflow occured if the current value in counter
// is smaller than the initial value
//
if ((CurrentCount & Mask) <
((ULONG64)(0 - ProfileSourceDescriptor->Interval) & Mask)) {
//
// Add it to the overflowed profile source list.
//
//
OverflowedProfileList[ProcessorNumber][j] = ProfileSource;
j++;
}
}
}
}
//
// Record the number of overflowed counters
//
pOverflowStatus->Number = j;
if(j) {
pOverflowStatus->pSource = &(OverflowedProfileList[ProcessorNumber][0]);
}
}
NTSTATUS
HalpGetProfileDescriptor(
IN KPROFILE_SOURCE ProfileSource,
IN OUT PAMD64_PROFILE_SOURCE_DESCRIPTOR *ProfileSourceDescriptor
)
/*++
Routine Description:
This function retrieves the descriptor of specified profile source.
Arguments:
ProfileSource - Supplies the profile source.
ProfileSourceDescriptor - Where the pointer of descriptor is returned.
Return Value:
STATUS_SUCCESS - If the requested mapping is found.
STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
--*/
{
LONG i;
if ((ULONG)ProfileSource < ProfileMaximum) {
//
// This is a generic profile source
//
i = ProfileSource;
}
else if ((ULONG)ProfileSource < ProfileAmd64Maximum &&
(ULONG)ProfileSource >= ProfileAmd64Minimum ) {
//
// This is an Amd64 specific profile source
//
i = ProfileSource - ProfileAmd64Minimum + ProfileMaximum;
}
else {
return STATUS_NOT_SUPPORTED;
}
*ProfileSourceDescriptor = &(Amd64ProfileSourceDescriptorTable[i]);
if (!((*ProfileSourceDescriptor)->Supported)) {
return STATUS_NOT_SUPPORTED;
}
return STATUS_SUCCESS;
}
NTSTATUS
HalpAllocateCounter(
IN KPROFILE_SOURCE ProfileSource,
OUT PULONG Counter
)
/*++
Routine Description:
This function finds an idle counter register and assigns the specified
profile source to it.
Arguments:
ProfileSource - Supplies the profile source.
Counter - Supplies a pointer where the index of the idle counter is returned.
Return Value:
STATUS_SUCCESS - If an idle counter register is found.
STATUS_DEVICE_BUSY - If all counter registers are occupied.
--*/
{
LONG i;
ULONG ProcessorNumber;
ProcessorNumber = KeGetCurrentProcessorNumber();
for(i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
if (CounterStatus[ProcessorNumber][i].Idle == TRUE) {
//
// Found an idle counter. Mark it busy and assign ProfileSource
// to it
//
CounterStatus[ProcessorNumber][i].Idle = FALSE;
CounterStatus[ProcessorNumber][i].ProfileSource = ProfileSource;
*Counter = i;
return STATUS_SUCCESS;
}
}
return STATUS_DEVICE_BUSY;
}
VOID
HalpFreeCounter(
ULONG Counter
)
/*++
Routine Description:
This function marks the specified counter idle.
Arguments:
Counter - The index of the counter to be freed.
Return Value:
None.
--*/
{
ULONG ProcessorNumber;
ProcessorNumber = KeGetCurrentProcessorNumber();
CounterStatus[ProcessorNumber][Counter].Idle = TRUE;
CounterStatus[ProcessorNumber][Counter].ProfileSource = 0;
}
NTSTATUS
Amd64QueryInformation(
IN HAL_QUERY_INFORMATION_CLASS InformationType,
IN ULONG BufferSize,
IN OUT PVOID Buffer,
OUT PULONG ReturnedLength
)
/*++
Routine Description:
This function retrieves the information of profile sources.
Arguments:
InformationClass - Constant that describes the type of information .
BufferSize - Size of the memory pointed to by Buffer.
Buffer - Requested information described by InformationClass.
ReturnedLength - The actual bytes returned or needed for the requested
information.
Return Value:
STATUS_SUCCESS - If the requested information is retrieved successfully.
STATUS_INFO_LENGTH_MISMATCH - If the incoming BufferSize is too small.
STATUS_NOT_SUPPORTED - If the specified information class or profile
source is not supported.
--*/
{
NTSTATUS Status;
ULONG i, TotalProfieSources;
PHAL_PROFILE_SOURCE_INFORMATION ProfileSourceInformation;
PHAL_PROFILE_SOURCE_LIST ProfileSourceList;
PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
switch (InformationType) {
case HalQueryProfileSourceList:
TotalProfieSources = sizeof(Amd64ProfileSourceDescriptorTable) /
sizeof(AMD64_PROFILE_SOURCE_DESCRIPTOR);
if (BufferSize == 0) {
//
// This indicates the caller just wants to know the
// size of the buffer to allocate but is not prepared
// to accept any data content. In this case the bytes
// needed to store HAL_PROFILE_SOURCE_LIST structures
// of all profile sources is returned.
//
*ReturnedLength = TotalProfieSources *
sizeof(HAL_PROFILE_SOURCE_LIST);
Status = STATUS_SUCCESS;
break;
}
if (BufferSize < TotalProfieSources *
sizeof(HAL_PROFILE_SOURCE_LIST)) {
*ReturnedLength = 0;
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
ProfileSourceList = (PHAL_PROFILE_SOURCE_LIST) Buffer;
for (i = 0; i < TotalProfieSources; i++) {
Status = HalpGetProfileDescriptor(i, &ProfileSourceDescriptor);
if (NT_SUCCESS(Status)) {
//
// Filling in requested data
//
ProfileSourceList->Source = ProfileSourceDescriptor->ProfileSource;
ProfileSourceList->Description = ProfileSourceDescriptor->Description;
ProfileSourceList++;
}
}
*ReturnedLength = (ULONG)((ULONG_PTR)ProfileSourceList -
(ULONG_PTR)Buffer);
Status = STATUS_SUCCESS;
break;
case HalProfileSourceInformation:
if (BufferSize < sizeof(HAL_PROFILE_SOURCE_INFORMATION)) {
*ReturnedLength = 0;
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
ProfileSourceInformation = (PHAL_PROFILE_SOURCE_INFORMATION) Buffer;
Status = HalpGetProfileDescriptor(ProfileSourceInformation->Source,
&ProfileSourceDescriptor);
if (!NT_SUCCESS(Status)) {
*ReturnedLength = 0;
Status = STATUS_NOT_SUPPORTED;
break;
}
//
// Filling in requested data
//
ProfileSourceInformation->Supported = ProfileSourceDescriptor->Supported;
ProfileSourceInformation->Interval = (ULONG) ProfileSourceDescriptor->Interval;
Status = STATUS_SUCCESS;
break;
default:
*ReturnedLength = 0;
Status = STATUS_NOT_SUPPORTED;
break;
}
return Status;
}