|
|
/*++
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; }
|