windows-nt/Source/XPSP1/NT/ds/win32/ntcrypto/randlib/cpu.c

497 lines
9.5 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
cpu.c
Abstract:
Read CPU specifics performance counters.
Author:
Scott Field (sfield) 24-Sep-98
--*/
#ifndef KMODE_RNG
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#else
#include <ntifs.h>
#include <windef.h>
#endif // KMODE_RNG
#include "cpu.h"
unsigned int
GatherCPUSpecificCountersPrivileged(
IN unsigned char *pbCounterState,
IN OUT unsigned long *pcbCounterState
);
#define X86_CAPS_RDTSC 0x01
#define X86_CAPS_RDMSR 0x02
#define X86_CAPS_RDPMC 0x04
VOID
X86_GetCapabilities(
IN OUT BYTE *pbCapabilities
);
VOID
X86_ReadRDTSC(
IN PLARGE_INTEGER prdtsc // RDTSC
);
VOID
X86_ReadRDMSR(
IN PLARGE_INTEGER pc0, // counter0
IN PLARGE_INTEGER pc1 // counter1
);
VOID
X86_ReadRDPMC(
IN PLARGE_INTEGER pc0, // counter0
IN PLARGE_INTEGER pc1 // counter1
);
#ifdef KMODE_RNG
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, GatherCPUSpecificCounters)
#pragma alloc_text(PAGE, GatherCPUSpecificCountersPrivileged)
#ifdef _X86_
#pragma alloc_text(PAGE, X86_ReadRDTSC)
#pragma alloc_text(PAGE, X86_ReadRDMSR)
#pragma alloc_text(PAGE, X86_ReadRDPMC)
#pragma alloc_text(PAGE, X86_GetCapabilities)
#endif // _X86_
#endif // ALLOC_PRAGMA
#endif // KMODE_RNG
unsigned int
GatherCPUSpecificCounters(
IN unsigned char *pbCounterState,
IN OUT unsigned long *pcbCounterState
)
{
#ifndef KMODE_RNG
//
// NT5 doesn't set CR4.PCE by default, so don't bother trying for usermode.
//
return FALSE;
#else
PAGED_CODE();
//
// kernel mode version of library: just call the privileged routine directly.
// user mode version of library: try privileged routine first, if it fails, use
// device driver provided interface.
//
if( pbCounterState == NULL || pcbCounterState == NULL )
return FALSE;
return GatherCPUSpecificCountersPrivileged( pbCounterState, pcbCounterState );
#endif
}
unsigned int
GatherCPUSpecificCountersPrivileged(
IN unsigned char *pbCounterState,
IN OUT unsigned long *pcbCounterState
)
/*++
we are at ring0 in kernel mode, so we can issue the privileges CPU
instructions directly. Note that this routine also serves as the
core code which is executed by the ksecdd.sys device driver for user
mode clients.
This call can also be made directly in usermode by certain CPUs,
or when certain CPUs are configured to allow such calls from ring3.
--*/
{
#ifdef _X86_
PLARGE_INTEGER prdtsc;
PLARGE_INTEGER pc0;
PLARGE_INTEGER pc1;
DWORD cbCounters;
BYTE ProcessorCaps;
#endif // _X86_
#ifdef KMODE_RNG
PAGED_CODE();
#endif // KMODE_RNG
#ifdef _X86_
cbCounters = 3 * sizeof( LARGE_INTEGER ) ;
if( *pcbCounterState < cbCounters ) {
*pcbCounterState = cbCounters;
return FALSE;
}
cbCounters = 0;
prdtsc = (PLARGE_INTEGER)pbCounterState;
pc0 = prdtsc + 1;
pc1 = pc0 + 1;
//
// make the initial determination about what the countertype is in this
// system.
// in theory, this could be cached, but things get a little complicated
// in an SMP machine -- we'd have to track caps across all processors
// to be correct. Since we don't really care about perf, just check the
// caps every time.
//
__try {
X86_GetCapabilities( &ProcessorCaps );
} __except( EXCEPTION_EXECUTE_HANDLER ) {
ProcessorCaps = 0;
; // swallow exception
}
//
// wrap the query in a try/except. This is just paranoia. Since we aren't
// particularly interested in the values of the perf counters for the normal
// (eg: perfmon) reasons, introducing the extra overhead of try/except is
// of no relevance to us.
// note that in the case of the p6, we could be calling this from usermode,
// and CR4.PCE could be toggled which could cause subsequent AV(s).
// In theory, the KMODE build could avoid the try/except, but there is a
// remote possiblity the code above which makes the initial countertype
// determination may not be supported on every installed processor in a
// SMP machine. The cost of try/except is well worth avoiding the possibility
// of a access violation / bluescreen in usermode vs. kernel mode respectively.
//
if( ProcessorCaps & X86_CAPS_RDTSC ) {
__try {
X86_ReadRDTSC( prdtsc );
cbCounters += sizeof( LARGE_INTEGER );
} __except( EXCEPTION_EXECUTE_HANDLER ) {
; // swallow exception.
}
}
if( ProcessorCaps & X86_CAPS_RDPMC ) {
__try {
X86_ReadRDPMC( pc0, pc1 );
cbCounters += (2*sizeof( LARGE_INTEGER ));
} __except( EXCEPTION_EXECUTE_HANDLER ) {
; // swallow exception.
}
}
#ifdef KMODE_RNG
else if ( ProcessorCaps & X86_CAPS_RDMSR ) {
__try {
X86_ReadRDMSR( pc0, pc1 );
cbCounters += (2*sizeof( LARGE_INTEGER ));
} __except( EXCEPTION_EXECUTE_HANDLER ) {
; // swallow exception.
}
}
#endif // KMODE_RNG
*pcbCounterState = cbCounters;
return TRUE;
#else // _X86_
//
// no non-x86 counter handling code at this time.
//
return FALSE;
#endif
}
#ifdef _X86_
VOID
X86_ReadRDTSC(
IN PLARGE_INTEGER prdtsc // RDTSC
)
{
DWORD prdtscLow ;
DWORD prdtscHigh ;
#ifdef KMODE_RNG
PAGED_CODE();
#endif // KMODE_RNG
__asm {
push eax
push ecx
push edx
// RDTSC
_emit 0fh
_emit 31h
mov prdtscLow, eax
mov prdtscHigh, edx
pop edx
pop ecx
pop eax
}
prdtsc->LowPart = prdtscLow;
prdtsc->HighPart = prdtscHigh;
}
VOID
X86_ReadRDMSR(
IN PLARGE_INTEGER pc0, // counter0
IN PLARGE_INTEGER pc1 // counter1
)
{
DWORD pc0Low ;
DWORD pc0High ;
DWORD pc1Low ;
DWORD pc1High ;
#ifdef KMODE_RNG
PAGED_CODE();
#endif // KMODE_RNG
__asm {
push eax
push ecx
push edx
// RDMSR counter0
mov ecx, 12h
_emit 0fh
_emit 32h
mov pc0Low, eax
mov pc0High, edx
// RDMSR counter1
mov ecx, 13h
_emit 0fh
_emit 32h
mov pc1Low, eax
mov pc1High, edx
pop edx
pop ecx
pop eax
}
pc0->LowPart = pc0Low;
pc0->HighPart = pc0High;
pc1->LowPart = pc1Low;
pc1->HighPart = pc1High;
}
VOID
X86_ReadRDPMC(
IN PLARGE_INTEGER pc0, // counter0
IN PLARGE_INTEGER pc1 // counter1
)
{
DWORD pc0Low ;
DWORD pc0High ;
DWORD pc1Low ;
DWORD pc1High ;
#ifdef KMODE_RNG
PAGED_CODE();
#endif // KMODE_RNG
__asm {
push eax
push ecx
push edx
// RDPMC executes from ring3 if CR4.PCE is set, otherwise, runs from ring0 only.
// RDPMC counter0
xor ecx, ecx
_emit 0fh
_emit 33h
mov pc0Low, eax
mov pc0High, edx
// RDPMC counter1
mov ecx, 1
_emit 0fh
_emit 33h
mov pc1Low, eax
mov pc1High, edx
pop edx
pop ecx
pop eax
}
pc0->LowPart = pc0Low;
pc0->HighPart = pc0High;
pc1->LowPart = pc1Low;
pc1->HighPart = pc1High;
}
#if _MSC_FULL_VER >= 13008827 && defined(_M_IX86)
#pragma warning(push)
#pragma warning(disable:4731) // EBP modified with inline asm
#endif
VOID
X86_GetCapabilities(
IN OUT BYTE *pbCapabilities
)
{
DWORD dwLevels;
DWORD dwStdFeatures;
DWORD dwVersionInfo;
DWORD Family;
DWORD Model;
#ifdef KMODE_RNG
PAGED_CODE();
#endif // KMODE_RNG
*pbCapabilities = 0;
__asm {
push eax
push ecx
push ebx
push edx
push edi
push esi
push ebp
xor eax, eax
_emit 0x0f
_emit 0xa2
mov dwLevels, eax
pop ebp
pop esi
pop edi
pop edx
pop ebx
pop ecx
pop eax
}
if( dwLevels == 0 )
return;
//
// try CPUID at level1 to get standard features.
//
__asm {
push eax
push ecx
push ebx
push edx
push edi
push esi
push ebp
mov eax, 1
_emit 0x0f
_emit 0xa2
mov dwVersionInfo, eax
mov dwStdFeatures, edx
pop ebp
pop esi
pop edi
pop edx
pop ebx
pop ecx
pop eax
}
//
// determine if RDTSC supported.
//
if( dwStdFeatures & 0x10 ) {
*pbCapabilities |= X86_CAPS_RDTSC;
}
Model = (dwVersionInfo >> 4) & 0xf;
Family = (dwVersionInfo >> 8) & 0xf;
// AMD K6-2 model 8 proved buggy and left interrupts disabled during RDMSR
#if 0
//
// determine if RDMSR supported.
//
if( dwStdFeatures & 0x20 && (Model == 1 || Model == 2) ) {
*pbCapabilities |= X86_CAPS_RDMSR;
}
//
// extract family, > pentium (family5) supports RDPMC
//
if( Family > 5 ) {
*pbCapabilities |= X86_CAPS_RDPMC;
}
#endif
}
#if _MSC_FULL_VER >= 13008827
#pragma warning(pop)
#endif
#endif // _X86_