diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /external/vpc/tier0/cputopology.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'external/vpc/tier0/cputopology.cpp')
| -rw-r--r-- | external/vpc/tier0/cputopology.cpp | 1018 |
1 files changed, 1018 insertions, 0 deletions
diff --git a/external/vpc/tier0/cputopology.cpp b/external/vpc/tier0/cputopology.cpp new file mode 100644 index 0000000..1125cae --- /dev/null +++ b/external/vpc/tier0/cputopology.cpp @@ -0,0 +1,1018 @@ +//------------------------------------------------------------------------------------- +// CpuTopology.cpp +// +// CpuToplogy class implementation. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------------- +#include "pch_tier0.h" + +#if defined(_WIN32) && !defined(_X360) && !defined( _PS3 ) +#include "cputopology.h" +#include <stdlib.h> +#include <crtdbg.h> + +#undef malloc +#undef free +#ifdef _WIN64 + // Inline assembly is not supported in 64-bit. Ideally, we would prefer to + // use the __cpuid intrinsic to avoid having to drop to assembly altogether. + // However, as of MSVC 9.0, the __cpuid intrinsic does not enable the + // ability to set the ECX register, which is required for obtaining certain + // extended CPU information. To overcome these issues, we must call the + // Cpuid64() external function, which is written in assembly and located in + // the cpuid64.asm file included in this project. This project contains a build + // step that invokes 64-bit MASM on cpuid64.asm when building a 64-bit target. + extern "C" void Cpuid64(void* argsPtr); +#endif + +//--------------------------------------------------------------------------------- +// Name: ICpuToplogy +// Desc: Specifies the interface that each class that provides an implementation +// for extracting cpu topology must conform to. This is the Implementor +// class in the traditional Bridge Pattern. +//--------------------------------------------------------------------------------- +class ICpuTopology +{ +public: + virtual ~ICpuTopology() + { + } + virtual BOOL IsDefaultImpl() const = 0; + virtual DWORD NumberOfProcessCores() const = 0; + virtual DWORD NumberOfSystemCores() const = 0; + virtual DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const = 0; +}; + + +namespace +{ +/////////////////////////////////////////////////////////////////////////////////// +// Local Class Definitions +/////////////////////////////////////////////////////////////////////////////////// + +//--------------------------------------------------------------------------------- +// Name: DefaultImpl +// Desc: Provides a default implementation for the ICpuTopology interface when +// GetLogicalProcessorInformation and CPUID are not supported for whatever +// reason. This is a ConcreteImplementor class in the traditional Bridge +// Pattern. +//--------------------------------------------------------------------------------- +class DefaultImpl : public ICpuTopology +{ +public: + //----------------------------------------------------------------------------- + // DefaultImpl::IsDefaultImpl + //----------------------------------------------------------------------------- + /*virtual*/ BOOL IsDefaultImpl() const + { + return TRUE; + } + + //----------------------------------------------------------------------------- + // DefaultImpl::NumberOfProcessCores + //----------------------------------------------------------------------------- + /*virtual*/ DWORD NumberOfProcessCores() const + { + return 1; + } + + //----------------------------------------------------------------------------- + // DefaultImpl::IsNumberOfSystemCores + //----------------------------------------------------------------------------- + /*virtual*/ DWORD NumberOfSystemCores() const + { + return 1; + } + + //----------------------------------------------------------------------------- + // DefaultImpl::CoreAffinityMask + //----------------------------------------------------------------------------- + /*virtual*/ DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const + { + DWORD_PTR coreAffinity = 0; + if( 1 == coreIdx ) + { + DWORD_PTR dwSystemAffinity; + GetProcessAffinityMask( GetCurrentProcess(), &coreAffinity, &dwSystemAffinity ); + } + return coreAffinity; + } +}; + +//--------------------------------------------------------------------------------- +// Name: GlpiImpl +// Desc: Provides the GetLogicalProcessorInformation implementation for the +// ICpuTopology interface. This is a ConcreteImplementor class in the +// traditional Bridge Pattern. +//--------------------------------------------------------------------------------- +class GlpiImpl : public ICpuTopology +{ +public: + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::GlpiImpl + // Desc: Initializes the internal structures/data with information retrieved + // from a call to GetLogicalProcessorInformation. + //----------------------------------------------------------------------------- + GlpiImpl() : m_pSlpi( NULL ), + m_nItems( 0 ) + { + _ASSERT( IsSupported() ); + + GlpiFnPtr pGlpi = GetGlpiFn_(); + _ASSERT( pGlpi ); + + DWORD cbBuffer = 0; + pGlpi( 0, &cbBuffer ); + + m_pSlpi = ( SYSTEM_LOGICAL_PROCESSOR_INFORMATION* )malloc( cbBuffer ); + pGlpi( m_pSlpi, &cbBuffer ); + m_nItems = cbBuffer / sizeof( SYSTEM_LOGICAL_PROCESSOR_INFORMATION ); + } + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::~GlpiImpl + //----------------------------------------------------------------------------- + /*virtual*/ ~GlpiImpl() + { + free( m_pSlpi ); + m_pSlpi = 0; + m_nItems = 0; + } + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::IsDefaultImpl + //----------------------------------------------------------------------------- + /*virtual*/ BOOL IsDefaultImpl() const + { + return FALSE; + } + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::NumberOfProcessCores + // Desc: Gets the total number of physical processor cores available to the + // current process. + //----------------------------------------------------------------------------- + /*virtual*/ DWORD NumberOfProcessCores() const + { + DWORD_PTR dwProcessAffinity, dwSystemAffinity; + GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity ); + + DWORD nCores = 0; + for( DWORD i = 0; i < m_nItems; ++i ) + { + if( ( RelationProcessorCore == m_pSlpi[i].Relationship ) && + ( m_pSlpi[i].ProcessorMask & dwProcessAffinity ) ) + { + ++nCores; + } + } + return nCores; + } + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::NumberOfSystemCores + // Desc: Gets the total number of physical processor cores enabled on the + // system. + //----------------------------------------------------------------------------- + /*virtual*/ DWORD NumberOfSystemCores() const + { + DWORD nCores = 0; + for( DWORD i = 0; i < m_nItems; ++i ) + { + if( RelationProcessorCore == m_pSlpi[i].Relationship ) + ++nCores; + } + return nCores; + } + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::CoreAffinityMask + // Desc: Gets an affinity mask that corresponds to the requested processor + // core. + //----------------------------------------------------------------------------- + /*virtual*/ DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const + { + DWORD_PTR dwProcessAffinity, dwSystemAffinity; + GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity ); + + for( DWORD i = 0; i < m_nItems; ++i ) + { + if( RelationProcessorCore == m_pSlpi[i].Relationship ) + { + if( !coreIdx-- ) + { + return m_pSlpi[i].ProcessorMask & dwProcessAffinity; + } + } + } + return 0; + } + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::IsSupported + //----------------------------------------------------------------------------- + static BOOL IsSupported() + { + return NULL != GetGlpiFn_(); + } + +private: + // GetLogicalProcessorInformation function pointer + typedef BOOL( WINAPI* GlpiFnPtr )( +SYSTEM_LOGICAL_PROCESSOR_INFORMATION*, +PDWORD +); + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::VerifyGlpiFn_ + // Desc: Gets a pointer to the GetLogicalProcessorInformation function only if + // it is supported on the current platform. + // GetLogicalProcessorInformation is supported on Windows Server 2003 and + // XP64, however there is a bug with the implementation. Therefore, only + // GetLogicalProcessorInformation on Windows Vista is supported in this + // sample. + //----------------------------------------------------------------------------- + static GlpiFnPtr VerifyGlpiFn_() + { + // VerifyVersionInfo function pointer + typedef BOOL ( WINAPI* VviFnPtr )( LPOSVERSIONINFOEX, + DWORD, + DWORDLONG ); + + HMODULE hMod = GetModuleHandle( TEXT( "kernel32" ) ); +#ifdef _UNICODE + VviFnPtr pVvi = (VviFnPtr) GetProcAddress( hMod, "VerifyVersionInfoW" ); + #else + VviFnPtr pVvi = ( VviFnPtr )GetProcAddress( hMod, "VerifyVersionInfoA" ); +#endif + GlpiFnPtr pGlpi = NULL; + + if( pVvi ) + { + // VerSetConditionMask function pointer + typedef ULONGLONG ( WINAPI* VscmFnPtr )( ULONGLONG, + DWORD, + BYTE ); + + VscmFnPtr pVscm = ( VscmFnPtr )GetProcAddress( hMod, "VerSetConditionMask" ); + + _ASSERT( pVscm ); + + // Check for Windows Vista + OSVERSIONINFOEX osvi = { sizeof( OSVERSIONINFOEX ) }; + osvi.dwMajorVersion = 6; + osvi.dwMinorVersion = 0; + osvi.wServicePackMajor = 0; + osvi.wServicePackMinor = 0; + + ULONGLONG dwlMask = 0; + dwlMask = pVscm( dwlMask, VER_MAJORVERSION, VER_GREATER_EQUAL ); + dwlMask = pVscm( dwlMask, VER_MINORVERSION, VER_GREATER_EQUAL ); + dwlMask = pVscm( dwlMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL ); + dwlMask = pVscm( dwlMask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL ); + + if( pVvi( &osvi, VER_MAJORVERSION + | VER_MINORVERSION + | VER_SERVICEPACKMAJOR + | VER_SERVICEPACKMINOR, + dwlMask ) ) + { + pGlpi = ( GlpiFnPtr )GetProcAddress( hMod, "GetLogicalProcessorInformation" ); + _ASSERT( pGlpi ); + } + } + + return pGlpi; + + } + + //----------------------------------------------------------------------------- + // Name: GlpiImpl::GetGlpiFn_ + // Desc: Gets a cached pointer to the GetLogicalProcessorInformation function. + //----------------------------------------------------------------------------- + static GlpiFnPtr GetGlpiFn_() + { + static GlpiFnPtr pGlpi = VerifyGlpiFn_(); + return pGlpi; + } + + // Private Members + SYSTEM_LOGICAL_PROCESSOR_INFORMATION* m_pSlpi; + DWORD m_nItems; +}; + +//--------------------------------------------------------------------------------- +// Name: ApicExtractor +// Desc: A utility class that provides an interface for decoding a processor +// APIC ID. An APIC ID is an 8-bit identifier given to each logical +// processor on system boot and can be retrieved by the CPUID instruction. +// Each APIC ID is composed of a PACKAGE_ID, CORE_ID and SMT_ID that describe +// the relationship of a logical processor within the processor topology of +// the system. +//--------------------------------------------------------------------------------- +class ApicExtractor +{ +public: + //----------------------------------------------------------------------------- + // Name: ApicExtractor::ApicExtractor + //----------------------------------------------------------------------------- + ApicExtractor( DWORD nLogProcsPerPkg = 1, DWORD nCoresPerPkg = 1 ) + { + SetPackageTopology( nLogProcsPerPkg, nCoresPerPkg ); + } + + //----------------------------------------------------------------------------- + // Name: ApicExtractor::SmtId + //----------------------------------------------------------------------------- + BYTE SmtId( BYTE apicId ) const + { + return apicId & m_smtIdMask.mask; + } + + //----------------------------------------------------------------------------- + // Name: ApicExtractor::CoreId + //----------------------------------------------------------------------------- + BYTE CoreId( BYTE apicId ) const + { + return ( apicId & m_coreIdMask.mask ) >> m_smtIdMask.width; + } + + //----------------------------------------------------------------------------- + // Name: ApicExtractor::PackageId + //----------------------------------------------------------------------------- + BYTE PackageId( BYTE apicId ) const + { + return ( apicId & m_pkgIdMask.mask ) >> + ( m_smtIdMask.width + m_coreIdMask.width ); + } + + //----------------------------------------------------------------------------- + // Name: ApicExtractor::PackageCoreId + //----------------------------------------------------------------------------- + BYTE PackageCoreId( BYTE apicId ) const + { + return ( apicId & ( m_pkgIdMask.mask | m_coreIdMask.mask ) ) >> + m_smtIdMask.width; + } + + //----------------------------------------------------------------------------- + // Name: ApicExtractor::GetLogProcsPerPkg + //----------------------------------------------------------------------------- + DWORD GetLogProcsPerPkg() const + { + return m_nLogProcsPerPkg; + } + + //----------------------------------------------------------------------------- + // Name: ApicExtractor::GetCoresPerPkg + //----------------------------------------------------------------------------- + DWORD GetCoresPerPkg() const + { + return m_nCoresPerPkg; + } + + //----------------------------------------------------------------------------- + // Name: ApicExtractor::SetPackageTopology + // Desc: You should call SetPackageTopology with the number of logical + // processors per package and number of cores per package before calling + // the sub id accessors (SmtId(), CoreId(), PackageId(), PackageCoreId()) + // as this information is required to effectively decode an APIC ID into + // its sub parts. + //----------------------------------------------------------------------------- + void SetPackageTopology( DWORD nLogProcsPerPkg, DWORD nCoresPerPkg ) + { + m_nLogProcsPerPkg = ( BYTE )nLogProcsPerPkg; + m_nCoresPerPkg = ( BYTE )nCoresPerPkg; + + // fix for Phenom x3 and similar CPUs - it reports 3 logical processors per package, and 4 cores per package + // so one core is probably just disabled for yield, but it causes a bug in GetMaskWidth that propagates + if( m_nCoresPerPkg > m_nLogProcsPerPkg ) + { + m_nCoresPerPkg = m_nLogProcsPerPkg; + } + + m_smtIdMask.width = GetMaskWidth_( m_nLogProcsPerPkg / m_nCoresPerPkg ); + m_coreIdMask.width = GetMaskWidth_( m_nCoresPerPkg ); + m_pkgIdMask.width = 8 - ( m_smtIdMask.width + m_coreIdMask.width ); + + m_pkgIdMask.mask = ( BYTE )( 0xFF << ( m_smtIdMask.width + m_coreIdMask.width ) ); + m_coreIdMask.mask = ( BYTE )( ( 0xFF << m_smtIdMask.width ) ^ m_pkgIdMask.mask ); + m_smtIdMask.mask = ( BYTE )~( 0xFF << m_smtIdMask.width ); + + } + +private: + //----------------------------------------------------------------------------- + // Name: ApicExtractor::GetMaskWidth_ + // Desc: Gets the width of a sub id bit field in an APIC ID. The width of a + // sub id (CORE_ID, SMT_ID) is only wide enough to support the maximum + // number of ids that needs to be represented in the topology. + //----------------------------------------------------------------------------- + static BYTE GetMaskWidth_( BYTE maxIds ) + { + --maxIds; + + // find index of msb + BYTE msbIdx = 8; + BYTE msbMask = 0x80; + while( msbMask && !( msbMask & maxIds ) ) + { + --msbIdx; + msbMask >>= 1; + } + return msbIdx; + } + + struct IdMask + { + BYTE width; + BYTE mask; + }; + + // Private Members + BYTE m_nLogProcsPerPkg; + BYTE m_nCoresPerPkg; + IdMask m_smtIdMask; + IdMask m_coreIdMask; + IdMask m_pkgIdMask; +}; + +//--------------------------------------------------------------------------------- +// Name: Cpuid +// Desc: A utility class that wraps the functionality of the CPUID instruction. +// Call the Call() method with the desired CPUID function, and use the +// register accessors to retrieve the register values. +//--------------------------------------------------------------------------------- +class Cpuid +{ +public: + // FnSet values are used to indicate a CPUID function set. + enum FnSet + { + Std = 0x00000000, + Ext = 0x80000000 + }; + + //----------------------------------------------------------------------------- + // Name: Cpuid::Cpuid + //----------------------------------------------------------------------------- + Cpuid() : m_eax( 0 ), + m_ebx( 0 ), + m_ecx( 0 ), + m_edx( 0 ) + { + } + + // Register accessors + DWORD Eax() const + { + return m_eax; + } + DWORD Ebx() const + { + return m_ebx; + } + DWORD Ecx() const + { + return m_ecx; + } + DWORD Edx() const + { + return m_edx; + } + + //----------------------------------------------------------------------------- + // Name: Cpuid::Call + // Desc: Calls the CPUID instruction with the specified function. Returns TRUE + // if the CPUID function was supported, FALSE if it wasn't. + //----------------------------------------------------------------------------- + BOOL Call( FnSet fnSet, DWORD fn ) + { + if( IsFnSupported( fnSet, fn ) ) + { + UncheckedCall_( fnSet, fn ); + return true; + } + return false; + } + + //----------------------------------------------------------------------------- + // Name: Cpuid::IsVendor + // Desc: Compares a string with the vendor string encoded in the CPUID + // instruction. + //----------------------------------------------------------------------------- + static BOOL IsVendor( const char* strVendor ) + { + // Cache the vendor string + static const Cpuid cpu( Std ); + return cpu.Ebx() == *reinterpret_cast<const DWORD*>( strVendor ) + && cpu.Ecx() == *reinterpret_cast<const DWORD*>( strVendor + 8 ) + && cpu.Edx() == *reinterpret_cast<const DWORD*>( strVendor + 4 ); + } + + //----------------------------------------------------------------------------- + // Name: Cpuid::IsFnSupported + // Desc: Checks to see if a CPUID function is supported. Different processors + // support different functions. This method is automatically called from + // the Call() method, so you don't need to call it beforehand. + //----------------------------------------------------------------------------- + static BOOL IsFnSupported( FnSet fnSet, DWORD fn ) + { + // Cache the maximum supported standard function + static const DWORD MaxStdFn = Cpuid( Std ).Eax(); + // Cache the maximum supported extended function + static const DWORD MaxExtFn = Cpuid( Ext ).Eax(); + + bool ret = false; + switch( fnSet ) + { + case Std: + ret = ( fn <= MaxStdFn ); + break; + case Ext: + ret = ( fn <= MaxExtFn ); + break; + default: + _ASSERT( 0 ); // should never get here + break; + } + return ret; + } + +private: + //----------------------------------------------------------------------------- + // Name: Cpuid::Cpuid + // Desc: This constructor is private and is only used to set a Cpuid object to + // initial values retrieved from CPUID functions 0x00000000 and + // 0x80000000. Good for caching values from the CPUID instruction that + // are not variable, like the encoded vendor string and the maximum + // supported CPUID function values. + //----------------------------------------------------------------------------- + explicit Cpuid( FnSet fnSet ) + { + UncheckedCall_( fnSet, 0 ); + } + + //----------------------------------------------------------------------------- + // Name: Cpuid::UncheckedCall_ + // Desc: Calls the CPUID instruction without checking for CPUID function + // support. + //----------------------------------------------------------------------------- + void UncheckedCall_( FnSet fnSet, DWORD fn ) + { +#ifdef _WIN64 + // Inline assembly is not supported in 64-bit. Ideally, we would prefer to + // use the __cpuid intrinsic to avoid having to drop to assembly altogether. + // However, as of MSVC 9.0, the __cpuid intrinsic does not enable the + // ability to set the ECX register, which is required for obtaining certain + // extended CPU information. To overcome these issues, we must call the + // Cpuid64() external function, which is written in assembly and located in + // the cpuid64.asm file included in this project. This project contains a build + // step that invokes 64-bit MASM on cpuid64.asm when building a 64-bit target. + m_eax = fnSet | fn; + m_ecx = 0; + Cpuid64(this); + #else + __asm + { + mov ecx, 0 + mov eax, fn + or eax, fnSet + cpuid + mov edi, this + mov [edi].m_eax, eax + mov [edi].m_ebx, ebx + mov [edi].m_ecx, ecx + mov [edi].m_edx, edx + } +#endif + } + + // Private Members + DWORD m_eax; + DWORD m_ebx; + DWORD m_ecx; + DWORD m_edx; +}; + +//--------------------------------------------------------------------------------- +// Name: CpuidImpl +// Desc: Provides the CPUID instruction implementation for the ICpuTopology +// interface. This is a ConcreteImplementor class in the traditional Bridge +// Pattern. +//--------------------------------------------------------------------------------- +class CpuidImpl : public ICpuTopology +{ +public: + // CpuidFnMasks are used when extracting bit-encoded information retrieved from + // the CPUID instruction + enum CpuidFnMasks + { + HTT = 0x10000000, // Fn0000_0001 EDX[28] + LogicalProcessorCount = 0x00FF0000, // Fn0000_0001 EBX[23:16] + ApicId = 0xFF000000, // Fn0000_0001 EBX[31:24] + NC_Intel = 0xFC000000, // Fn0000_0004 EAX[31:26] + NC_Amd = 0x000000FF, // Fn8000_0008 ECX[7:0] + CmpLegacy_Amd = 0x00000002, // Fn8000_0001 ECX[1] + ApicIdCoreIdSize_Amd = 0x0000F000 // Fn8000_0008 ECX[15:12] + }; + + enum + { + MaxLogicalProcessors = sizeof( DWORD_PTR ) * 8 + }; + + //----------------------------------------------------------------------------- + // Name: CpuidImpl::CpuidImpl + // Desc: Initializes internal structures/data with information retrieved from + // calling the CPUID instruction. + //----------------------------------------------------------------------------- + CpuidImpl() : m_nItems( 0 ) + { + _ASSERT( IsSupported() ); + + DWORD nLogProcsPerPkg = 1; + DWORD nCoresPerPkg = 1; + + Cpuid cpu; + + // Determine if hardware threading is enabled. + cpu.Call( Cpuid::Std, 1 ); + if( cpu.Edx() & HTT ) + { + // Determine the total number of logical processors per package. + nLogProcsPerPkg = ( cpu.Ebx() & LogicalProcessorCount ) >> 16; + + // Determine the total number of cores per package. This info + // is extracted differently dependending on the cpu vendor. + if( Cpuid::IsVendor( GenuineIntel ) ) + { + if( cpu.Call( Cpuid::Std, 4 ) ) + { + nCoresPerPkg = ( ( cpu.Eax() & NC_Intel ) >> 26 ) + 1; + } + } + else + { + _ASSERT( Cpuid::IsVendor( AuthenticAMD ) ); + if( cpu.Call( Cpuid::Ext, 8 ) ) + { + // AMD reports the msb width of the CORE_ID bit field of the APIC ID + // in ApicIdCoreIdSize_Amd. The maximum value represented by the msb + // width is the theoretical number of cores the processor can support + // and not the actual number of current cores, which is how the msb width + // of the CORE_ID bit field has been traditionally determined. If the + // ApicIdCoreIdSize_Amd value is zero, then you use the traditional method + // to determine the CORE_ID msb width. + DWORD msbWidth = cpu.Ecx() & ApicIdCoreIdSize_Amd; + if( msbWidth ) + { + // Set nCoresPerPkg to the maximum theortical number of cores + // the processor package can support (2 ^ width) so the APIC + // extractor object can be configured to extract the proper + // values from an APIC. + nCoresPerPkg = 1 << ( msbWidth >> 12 ); + } + else + { + // Set nCoresPerPkg to the actual number of cores being reported + // by the CPUID instruction. + nCoresPerPkg = ( cpu.Ecx() & NC_Amd ) + 1; + } + } + } + } + + // Configure the APIC extractor object with the information it needs to + // be able to decode the APIC. + m_apicExtractor.SetPackageTopology( nLogProcsPerPkg, nCoresPerPkg ); + + DWORD_PTR dwProcessAffinity, dwSystemAffinity; + HANDLE hProcess = GetCurrentProcess(); + HANDLE hThread = GetCurrentThread(); + GetProcessAffinityMask( hProcess, &dwProcessAffinity, &dwSystemAffinity ); + if( 1 == dwSystemAffinity ) + { + // Since we only have 1 logical processor present on the system, we + // can explicitly set a single APIC ID to zero. + _ASSERT( 1 == nLogProcsPerPkg ); + m_apicIds[m_nItems++] = 0; + } + else + { + // Set the process affinity to the system affinity if they are not + // equal so that all logical processors can be accounted for. + if( dwProcessAffinity != dwSystemAffinity ) + { + SetProcessAffinityMask( hProcess, dwSystemAffinity ); + } + + // Call cpuid on each active logical processor in the system affinity. + DWORD_PTR dwPrevThreadAffinity = 0; + for( DWORD_PTR dwThreadAffinity = 1; + dwThreadAffinity && dwThreadAffinity <= dwSystemAffinity; + dwThreadAffinity <<= 1 ) + { + if( dwSystemAffinity & dwThreadAffinity ) + { + if( 0 == dwPrevThreadAffinity ) + { + // Save the previous thread affinity so we can return + // the executing thread affinity back to this state. + _ASSERT( 0 == m_nItems ); + dwPrevThreadAffinity = SetThreadAffinityMask( hThread, + dwThreadAffinity ); + } + else + { + _ASSERT( m_nItems > 0 ); + SetThreadAffinityMask( hThread, dwThreadAffinity ); + } + + // Allow the thread to switch to masked logical processor. + Sleep( 0 ); + + // Store the APIC ID + cpu.Call( Cpuid::Std, 1 ); + m_apicIds[m_nItems++] = ( BYTE )( ( cpu.Ebx() & ApicId ) >> 24 ); + } + } + + // Restore the previous process and thread affinity state. + SetProcessAffinityMask( hProcess, dwProcessAffinity ); + SetThreadAffinityMask( hThread, dwPrevThreadAffinity ); + Sleep( 0 ); + } + + } + + //----------------------------------------------------------------------------- + // Name: CpuidImpl::IsDefaultImpl + //----------------------------------------------------------------------------- + /*virtual*/ BOOL IsDefaultImpl() const + { + return FALSE; + } + + //----------------------------------------------------------------------------- + // Name: CpuidImpl::NumberOfProcessCores + // Desc: Gets the number of processor cores available to the current process. + // The total accounts for cores that may have been masked out by process + // affinity. + //----------------------------------------------------------------------------- + /*virtual*/ DWORD NumberOfProcessCores() const + { + DWORD_PTR dwProcessAffinity, dwSystemAffinity; + GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity ); + + BYTE pkgCoreIds[MaxLogicalProcessors] = { 0 }; + DWORD nPkgCoreIds = 0; + + for( DWORD i = 0; i < m_nItems; ++i ) + { + if( dwProcessAffinity & ( ( DWORD_PTR )1 << i ) ) + { + AddUniquePkgCoreId_( i, pkgCoreIds, nPkgCoreIds ); + } + } + return nPkgCoreIds; + } + + //----------------------------------------------------------------------------- + // Name: CpuidImpl::NumberOfSystemCores + // Desc: Gets the number of processor cores on the system. + //----------------------------------------------------------------------------- + /*virtual*/ DWORD NumberOfSystemCores() const + { + BYTE pkgCoreIds[MaxLogicalProcessors] = { 0 }; + DWORD nPkgCoreIds = 0; + for( DWORD i = 0; i < m_nItems; ++i ) + { + AddUniquePkgCoreId_( i, pkgCoreIds, nPkgCoreIds ); + } + return nPkgCoreIds; + } + + //----------------------------------------------------------------------------- + // Name: CpuidImpl::CoreAffinityMask + // Desc: Gets an affinity mask that corresponds to a specific processor core. + // coreIdx must be less than the total number of processor cores + // recognized by the operating system (NumberOfSystemCores()). + //----------------------------------------------------------------------------- + /*virtual*/ DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const + { + BYTE pkgCoreIds[MaxLogicalProcessors] = { 0 }; + DWORD nPkgCoreIds = 0; + for( DWORD i = 0; i < m_nItems; ++i ) + { + AddUniquePkgCoreId_( i, pkgCoreIds, nPkgCoreIds ); + } + + DWORD_PTR dwProcessAffinity, dwSystemAffinity; + GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity ); + + DWORD_PTR coreAffinity = 0; + if( coreIdx < nPkgCoreIds ) + { + for( DWORD i = 0; i < m_nItems; ++i ) + { + if( m_apicExtractor.PackageCoreId( m_apicIds[i] ) == pkgCoreIds[coreIdx] ) + { + coreAffinity |= ( dwProcessAffinity & ( ( DWORD_PTR )1 << i ) ); + } + } + } + return coreAffinity; + } + + //----------------------------------------------------------------------------- + // Name: CpuidImpl::IsSupported + // Desc: Indicates if a CpuidImpl object is supported on this platform. + // Support is only granted on Intel and AMD platforms where the current + // calling process has security rights to query process affinity and + // change it if the process and system affinity differ. CpuidImpl is + // also not supported if thread affinity cannot be set on systems with + // more than 1 logical processor. + //----------------------------------------------------------------------------- + static BOOL IsSupported() + { + BOOL bSupported = Cpuid::IsVendor( GenuineIntel ) + || Cpuid::IsVendor( AuthenticAMD ); + + if( bSupported ) + { + DWORD_PTR dwProcessAffinity, dwSystemAffinity; + HANDLE hProcess = GetCurrentProcess(); + + // Query process affinity mask + bSupported = GetProcessAffinityMask( hProcess, &dwProcessAffinity, &dwSystemAffinity ); + if( bSupported ) + { + if( dwProcessAffinity != dwSystemAffinity ) + { + // The process and system affinities differ. Attempt to set + // the process affinity to the system affinity. + bSupported = SetProcessAffinityMask( hProcess, dwSystemAffinity ); + if( bSupported ) + { + // Restore previous process affinity + bSupported = SetProcessAffinityMask( hProcess, dwProcessAffinity ); + } + } + + if( bSupported && ( dwSystemAffinity > 1 ) ) + { + // Attempt to set the thread affinity + HANDLE hThread = GetCurrentThread(); + DWORD_PTR dwThreadAffinity = SetThreadAffinityMask( hThread, dwProcessAffinity ); + if( dwThreadAffinity ) + { + // Restore the previous thread affinity + bSupported = 0 != SetThreadAffinityMask( hThread, dwThreadAffinity ); + } + else + { + bSupported = FALSE; + } + } + } + } + return bSupported; + } + +private: + + //----------------------------------------------------------------------------- + // Name: CpuidImpl::AddUniquePkgCoreId_ + // Desc: Adds the package/core id extracted from the APIC ID at m_apicIds[idx] + // in the if the package/core id is unique to the pkgCoreIds array. + // nPkgCore is an in/out parm that will reflect the total number of items + // in pkgCoreIds array. It will be incrememted if a unique package/core + // id is found and added. + //----------------------------------------------------------------------------- + void AddUniquePkgCoreId_( DWORD idx, BYTE* pkgCoreIds, DWORD& nPkgCoreIds ) const + { + _ASSERT( idx < m_nItems ); + _ASSERT( NULL != pkgCoreIds ); + + DWORD j; + for( j = 0; j < nPkgCoreIds; ++j ) + { + if( pkgCoreIds[j] == m_apicExtractor.PackageCoreId( m_apicIds[idx] ) ) + break; + } + if( j == nPkgCoreIds ) + { + pkgCoreIds[j] = m_apicExtractor.PackageCoreId( m_apicIds[idx] ); + ++nPkgCoreIds; + } + } + + // Private Members + BYTE m_apicIds[MaxLogicalProcessors]; + BYTE m_nItems; + ApicExtractor m_apicExtractor; + + // Supported Vendor Strings + static const char GenuineIntel[]; + static const char AuthenticAMD[]; +}; + +// Static initialization of vendor strings +const char CpuidImpl::GenuineIntel[] = "GenuineIntel"; +const char CpuidImpl::AuthenticAMD[] = "AuthenticAMD"; + +} // unnamed-namespace + +//------------------------------------------------------------------------------------- +// Name: CpuTopology::CpuTopology +// Desc: Initializes this object with the appropriately supported cpu topology +// implementation object. +//------------------------------------------------------------------------------------- +CpuTopology::CpuTopology( BOOL bForceCpuid ) : m_pImpl( NULL ) +{ + ForceCpuid( bForceCpuid ); +} + +//------------------------------------------------------------------------------------- +// Name: CpuTopology::~CpuTopology +//------------------------------------------------------------------------------------- +CpuTopology::~CpuTopology() +{ + Destroy_(); +} + +//------------------------------------------------------------------------------------- +// Name: CpuTopology::NumberOfProcessCores +// Desc: Gets the total number of physical processor cores available to the current +// process. +//------------------------------------------------------------------------------------- +DWORD CpuTopology::NumberOfProcessCores() const +{ + return m_pImpl->NumberOfProcessCores(); +} + +//------------------------------------------------------------------------------------- +// Name: CpuTopology::NumberOfSystemCores +// Desc: Gets the total number of physical processor cores enabled on the system. +//------------------------------------------------------------------------------------- +DWORD CpuTopology::NumberOfSystemCores() const +{ + return m_pImpl->NumberOfSystemCores(); +} + +//------------------------------------------------------------------------------------- +// Name: CpuTopology::CoreAffinityMask +// Desc: Gets an affinity mask that corresponds to the requested processor core. +//------------------------------------------------------------------------------------- +DWORD_PTR CpuTopology::CoreAffinityMask( DWORD coreIdx ) const +{ + return m_pImpl->CoreAffinityMask( coreIdx ); +} + +//------------------------------------------------------------------------------------- +// Name: CpuTopology::IsDefaultImpl +// Desc: Returns TRUE if m_pImpl is a DefaultImpl object, FALSE if not. Used to +// indicate whether or not the prescribed methods (CPUID or +// GetLogicalProcessorInformation) are supported on the system. +//------------------------------------------------------------------------------------- +BOOL CpuTopology::IsDefaultImpl() const +{ + return m_pImpl->IsDefaultImpl(); +} + +//------------------------------------------------------------------------------------- +// Name: CpuTopology::ForceCpuid +// Desc: Constructs a cpu topology object. If bForce is FALSE, then a GlpiImpl object +// is first attempted, then CpuidImpl, then finally DefaultImpl. If bForce is +// TRUE, then GlpiImpl is never attempted. +//------------------------------------------------------------------------------------- +void CpuTopology::ForceCpuid( BOOL bForce ) +{ + Destroy_(); + + if( !bForce && GlpiImpl::IsSupported() ) + { + m_pImpl = new GlpiImpl(); + } + else if( CpuidImpl::IsSupported() ) + { + m_pImpl = new CpuidImpl(); + } + else + { + m_pImpl = new DefaultImpl(); + } +} + +//------------------------------------------------------------------------------------- +// Name: CpuTopology::Destroy_ +//------------------------------------------------------------------------------------- +void CpuTopology::Destroy_() +{ + delete m_pImpl; + m_pImpl = NULL; +} +#endif
\ No newline at end of file |