JD2022-TU1/main/extern/gear4/gear_detection/detection/gearcpu.cpp

755 lines
26 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// File : gearcpu.cpp - GEAR cpu detection
// Copyright (c) Ubisoft Entertainment. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////
#include "gearcpu.h"
#include "gearhardware.h"
#include "systeminc.h"
#include <gear_core/base/str.h>
namespace G4 {
namespace SystemDetection
{
namespace Private
{
class CPUInfo
{
public:
CPUInfo();
U32 MaxNumberOfThread() const
{
return (function1.edx.bits.HTT == 0 ? 1 : function1.ebx.bits.LogicalProcessorCount);
}
U32 MaxNumberOfCore() const
{
return (function1.edx.bits.HTT == 0 ? 1 : 1 + function4.eax.bits.CorePerPackage);
}
// Function 0
struct Function0
{
union EaxRegister
{
struct EaxBits {
U32 LARGEST_STD_FUNCTION;
} bits;
U32 raw;
} eax;
} function0;
// Function 1
struct Function1
{
union EaxRegister
{
struct EaxBits
{
U32 STEPPING_ID : 4;
U32 MODEL : 4;
U32 FAMILY : 4;
U32 TYPE : 2;
U32 : 2;
U32 EXT_MODEL : 4;
U32 EXT_FAMILY : 8;
U32 : 4;
} bits;
U32 raw;
} eax;
union EbxRegister
{
struct EbxBits
{
U32 BrandId : 8;
U32 CLFlush : 8;
U32 LogicalProcessorCount : 8;
U32 LocalApicId : 8;
} bits;
U32 raw;
} ebx;
union EcxRegister
{
struct EcxBits
{
U32 SSE3 : 1;
U32 : 1;
U32 DTES64 : 1;
U32 MONITOR : 1;
U32 DS_CPL : 1;
U32 VMX : 1;
U32 SMX : 1;
U32 EST : 1;
U32 TM2 : 1;
U32 SSSE3 : 1;
U32 CNXTID : 1;
U32 : 1;
U32 : 1;
U32 CX16 : 1;
U32 xTPR : 1;
U32 PDCM : 1;
U32 : 1;
U32 : 1;
U32 DCA : 1;
U32 SSE41 : 1;
U32 SSE42 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
U32 : 1;
} bits;
U32 raw;
} ecx;
union EdxRegister
{
struct EdxBits
{
U32 FPU : 1;
U32 VME : 1;
U32 DE : 1;
U32 PSE : 1;
U32 TSC : 1;
U32 MSR : 1;
U32 PAE : 1;
U32 MCE : 1;
U32 CX8 : 1;
U32 APIC : 1;
U32 : 1;
U32 SEP : 1;
U32 MTRR : 1;
U32 PGE : 1;
U32 MCA : 1;
U32 CMOV : 1;
U32 PAT : 1;
U32 PSE36 : 1;
U32 PSN : 1;
U32 CLFSH : 1;
U32 : 1;
U32 DS : 1;
U32 ACPI : 1;
U32 MMX : 1;
U32 FXSR : 1;
U32 SSE : 1;
U32 SSE2 : 1;
U32 SS : 1;
U32 HTT : 1;
U32 TM : 1;
U32 IA64 : 1;
U32 PBE : 1;
} bits;
U32 raw;
} edx;
} function1;
struct Function4
{
union EaxRegister
{
struct EaxBits
{
U32 : 26;
U32 CorePerPackage : 6;
} bits;
U32 raw;
} eax;
} function4;
Char vendorName[16];
Char processorName[48];
};
CPUInfo::CPUInfo()
{
int info[4] = { 0, 0, 0, 0 };
// Reset the members
System::memzero(&function0, sizeof(function0));
System::memzero(&function1, sizeof(function1));
System::memzero(&function4, sizeof(function4));
int* vendorNamePtr = (int*)vendorName;
System::memzero(vendorNamePtr, sizeof(vendorName));
int* processorNamePtr = (int*)processorName;
System::memzero(processorNamePtr, sizeof(processorName));
// Fetch the function 0 values
__cpuid(info, 0);
function0.eax.raw = info[0]; // largest standard function supported
vendorNamePtr[0] = info[1];
vendorNamePtr[1] = info[3]; // Not a mistake. Order is 1, 3, 2!
vendorNamePtr[2] = info[2];
// Fetch the function 1 values
__cpuid(info, 1);
function1.eax.raw = info[0];
function1.ebx.raw = info[1];
function1.ecx.raw = info[2];
function1.edx.raw = info[3];
// Fetch the function 0x80000000 values
__cpuid(info, 0x80000000);
if (info[0] > 0x80000004) // info[0] indicate the supported function of cpuid for brand name and others
{
__cpuid(processorNamePtr + 0, 0x80000002); // first 16 bytes
__cpuid(processorNamePtr + 4, 0x80000003); // second 16 bytes
__cpuid(processorNamePtr + 8, 0x80000004); // last 16 bytes
}
// Fetch the function 4 values, if function 4 is supported
if (function0.eax.bits.LARGEST_STD_FUNCTION >= 4)
{
// We must reset ECX to ensure cpuid returns the expected values
ResetECX();
__cpuid(info, 4);
function4.eax.raw = info[0];
}
}
// Determine the width of the bit field that can represent the value count_item.
U32 FindMaskWidth(U32 CountItem)
{
U32 index = 0;
U32 current = CountItem - 1;
while (current > 0)
{
current >>= 1;
index++;
}
return index;
}
// Extract the subset of bit field from the 8-bit value fullId. It returns the 8-bit sub ID value
U8 GetNzbSubID(U8 fullId, U32 maxSubIdValue, U8 shiftCount)
{
U32 maskWidth;
U8 maskBits;
maskWidth = FindMaskWidth(maxSubIdValue);
maskBits = (0xff << shiftCount) ^ ((U8) (0xff << (shiftCount + maskWidth)));
return (fullId & maskBits);
}
bool CPUCount(U32 *numberOfLogicalProcessors,
U32 *numberOfCores,
U32 *numberOfPhysicalProcessors)
{
U32 availableLogicalProcessors;
// The following variables are parameters to Win32 API calls
DWORD_PTR dwAffinityMask;
DWORD_PTR dwProcessAffinity;
DWORD_PTR dwSystemAffinity;
U32 cpuIndex, maximumLogicalProcessorPerCore;
U8 apicID, packageIdMask;
U8 packageIdTable[256];
U8 coreIdTable[256] = {0};
CPUInfo defaultCpu;
// Set the values we know, based on the freshly created CPUInfo object
*numberOfPhysicalProcessors = 1; // Best guess
*numberOfCores = defaultCpu.MaxNumberOfCore();
*numberOfLogicalProcessors = defaultCpu.MaxNumberOfThread();
HANDLE hCurrentProcess = GetCurrentProcess();
GetProcessAffinityMask(
hCurrentProcess,
&dwProcessAffinity,
&dwSystemAffinity);
if (dwProcessAffinity != dwSystemAffinity)
{
// Not all CPUs are enabled
return false;
}
// Assume that cores within a package have the SAME number of logical processors.
// Also, values returned by MaxNumberOfThread and MaxNumberOfCore do not have to be power of 2.
maximumLogicalProcessorPerCore = defaultCpu.MaxNumberOfThread() / defaultCpu.MaxNumberOfCore();
availableLogicalProcessors = 0;
dwAffinityMask = 1;
cpuIndex = 0;
HANDLE hCurrentThread = GetCurrentThread();
while (dwAffinityMask && dwAffinityMask <= dwSystemAffinity)
{
if (SetThreadAffinityMask(hCurrentThread, dwAffinityMask))
{
Sleep(0); // Ensure system to switch to the right CPUInfo
CPUInfo currentCpu; // Object is created on a specific CPU
apicID = currentCpu.function1.ebx.bits.LocalApicId;
coreIdTable[cpuIndex] = GetNzbSubID(
apicID,
currentCpu.MaxNumberOfCore(),
(U8)FindMaskWidth(maximumLogicalProcessorPerCore));
// Extract package ID, assume single cluster.
// Shift value is the mask width for max Logical per package
packageIdMask = (U8) (0xff <<
FindMaskWidth(currentCpu.MaxNumberOfThread()));
packageIdTable[cpuIndex] = apicID & packageIdMask;
availableLogicalProcessors++; // Number of available logical processors in the system.
} // if
cpuIndex++;
dwAffinityMask = (DWORD_PTR)(1 << cpuIndex);
} // while
// Important: restore the affinity setting to its original state
SetThreadAffinityMask(hCurrentThread, dwProcessAffinity);
Sleep(0);
*numberOfLogicalProcessors = availableLogicalProcessors;
//
// Count available cores (numberOfCores) in the system
//
U8 CoreIDBucket[256];
DWORD processorMask, pCoreMask[256];
U32 i, processorNumber;
CoreIDBucket[0] = packageIdTable[0] | coreIdTable[0];
processorMask = 1;
pCoreMask[0] = processorMask;
*numberOfCores = 1;
for (processorNumber = 1; processorNumber < availableLogicalProcessors; processorNumber++)
{
processorMask <<= 1;
for (i = 0; i < *numberOfCores; i++)
{
// Comparing bit-fields of logical processors residing in different packages
// Assuming the bit-masks are the same on all processors in the system.
if ((packageIdTable[processorNumber] | coreIdTable[processorNumber]) == CoreIDBucket[i])
{
pCoreMask[i] |= processorMask;
break;
}
} // for i
if (i == *numberOfCores) // did not match any bucket. Start a new one.
{
CoreIDBucket[i] = packageIdTable[processorNumber] | coreIdTable[processorNumber];
pCoreMask[i] = processorMask;
(*numberOfCores)++; // Number of available cores in the system
}
} // for processorNumber
//
// Count physical processor (numberOfPhysicalProcessors) in the system
//
U8 packageIdBucket[256];
DWORD pPackageMask[256];
packageIdBucket[0] = packageIdTable[0];
processorMask = 1;
pPackageMask[0] = processorMask;
for (processorNumber = 1; processorNumber < availableLogicalProcessors; processorNumber++)
{
processorMask <<= 1;
for (i = 0; i < *numberOfPhysicalProcessors; i++)
{
// Comparing bit-fields of logical processors residing in different packages
// Assuming the bit-masks are the same on all processors in the system.
if (packageIdTable[processorNumber]== packageIdBucket[i])
{
pPackageMask[i] |= processorMask;
break;
}
} // for i
if (i == *numberOfPhysicalProcessors) // did not match any bucket. Start a new one.
{
packageIdBucket[i] = packageIdTable[processorNumber];
pPackageMask[i] = processorMask;
(*numberOfPhysicalProcessors)++; // Total number of physical processors in the system
}
} // for processorNumber
return true;
}
typedef struct _LOGICALPROCESSORDATA
{
unsigned int nLargestStandardFunctionNumber;
unsigned int nLargestExtendedFunctionNumber;
int nLogicalProcessorCount;
int nLocalApicId;
int nCPUcore;
int nProcessorId;
int nApicIdCoreIdSize;
int nNC;
int nMNC;
int nCPUCoresperProcessor;
int nThreadsperCPUCore;
int nProcId;
int nCoreId;
bool CmpLegacy;
bool HTT;
} LOGICALPROCESSORDATA, *PLOGICALPROCESSORDATA;
const U32 MAX_NUMBER_OF_LOGICAL_PROCESSORS = 96;
const U32 MAX_NUMBER_OF_PHYSICAL_PROCESSORS = 8;
const U32 MAX_NUMBER_OF_IOAPICS = 16;
int QueryNumLogicalProcessors( void )
{
SYSTEM_INFO siSysInfo;
GetSystemInfo( &siSysInfo );
return( siSysInfo.dwNumberOfProcessors );
}
int whichcpu( void )
{
int CPUInfo[4] = {0,0,0,0};
__cpuid( CPUInfo, 1 );
return ((CPUInfo[1] >> 24) & 0xff);
}
Bool GetProcessAffinity(DWORD& process, DWORD& system)
{
#if defined(WIN64)
DWORD_PTR ProcessAffinityMask;
DWORD_PTR SystemAffinityMask;
#else
DWORD ProcessAffinityMask;
DWORD SystemAffinityMask;
#endif
BOOL rc = GetProcessAffinityMask( GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask );
if(rc)
{
process = (DWORD)ProcessAffinityMask;
system = (DWORD)SystemAffinityMask;
}
return (rc == TRUE);
}
Bool SetProcessAffinity(DWORD process)
{
#if defined(WIN64)
DWORD_PTR ProcessAffinityMask;
#else
DWORD ProcessAffinityMask;
#endif
ProcessAffinityMask = process;
return (SetProcessAffinityMask( GetCurrentProcess(), ProcessAffinityMask) == TRUE);
}
Bool LockToLogicalProcessor( int n ) /* I want to just stick onto one particular core */
{
DWORD ProcessAffinityMask;
DWORD SystemAffinityMask;
Bool rc = GetProcessAffinity(ProcessAffinityMask, SystemAffinityMask);
if(rc)
{
DWORD pm, pmm;
pm = (DWORD)SystemAffinityMask;
pmm = 1;
while (n)
{
pmm = pmm << 1;
n--;
}
rc = SetProcessAffinity(pmm);
}
return rc;
}
void cpuid( int whichlp, LOGICALPROCESSORDATA *LogicalProcessorMap )
{
unsigned int i, j, mask, numbits;
PLOGICALPROCESSORDATA p;
int CPUInfo[4] = {0,0,0,0};
p = &LogicalProcessorMap[whichlp];
LockToLogicalProcessor( whichlp );
__cpuid(CPUInfo, 0);
p->nLargestStandardFunctionNumber = CPUInfo[0];
// Get the information associated with each valid Id
for (i=0; i <= p->nLargestStandardFunctionNumber; ++i)
{
__cpuid( CPUInfo, i );
// Interpret CPU feature information.
if (i == 1)
{
// Some of the bits of LocalApicId represent the CPU core
// within a processor and other bits represent the processor ID.
p->nLocalApicId = (CPUInfo[1] >> 24) & 0xff;
p->HTT = (CPUInfo[3] >> 28) & 0x1;
// recalculate later after 0x80000008
p->nLogicalProcessorCount = (CPUInfo[1] >> 16) & 0x0FF;
}
}
// Calling __cpuid with 0x80000000 as the InfoType argument
// gets the number of valid extended IDs.
__cpuid( CPUInfo, 0x80000000 );
p->nLargestExtendedFunctionNumber = CPUInfo[0];
// Get the information associated with each extended ID.
for (i=0x80000000; i<=p->nLargestExtendedFunctionNumber; ++i)
{
__cpuid( CPUInfo, i );
if (i == 0x80000008)
{
p->nApicIdCoreIdSize = (CPUInfo[2] >> 12) & 0xF;
p->nNC = (CPUInfo[2]) & 0x0FF;
}
}
// MNC
// A value of zero for ApicIdCoreIdSize indicates that MNC is derived by this
// legacy formula: MNC = NC + 1
// A non-zero value of ApicIdCoreIdSize means that MNC is 2^ApicIdCoreIdSize
if (p->nApicIdCoreIdSize)
{
p->nMNC = 2;
for (j = p->nApicIdCoreIdSize-1; j>0; j--)
p->nMNC = p->nMNC * 2;
}
else
{
p->nMNC = p->nNC + 1;
}
// If HTT==0, then LogicalProcessorCount is reserved, and the CPU contains
// one CPU core and the CPU core is single-threaded.
// If HTT==1 and CmpLegacy==1, LogicalProcessorCount represents the number of
// CPU cores per processor, where each CPU core is single-threaded. If HTT==1
// and CmpLegacy==0, then LogicalProcessorCount is the number of threads per
// processor, which is the number of cores times the number of threads per core.
// The number of cores is NC+1.
p->nCPUCoresperProcessor = p->nNC + 1;
p->nThreadsperCPUCore = ( p->HTT==0 ? 1 :
( p->CmpLegacy==1 ? 1 :
p->nLogicalProcessorCount / p->nCPUCoresperProcessor
)
);
// Calculate a mask for the core IDs
mask = 1;
numbits = 1;
if (p->nApicIdCoreIdSize)
{
numbits = p->nApicIdCoreIdSize;
for (j = p->nApicIdCoreIdSize; j>1; j--)
mask = (mask << 1) + 1;
}
p->nProcId = p->nLocalApicId & ~mask;
p->nProcId = p->nProcId >> (numbits);
p->nCoreId = p->nLocalApicId & mask;
}
Bool AMDCPUCount(U32 *numberOfLogicalProcessors,
U32 *numberOfCores,
U32 *numberOfPhysicalProcessors)
{
int nlp, num_processors, i;
LOGICALPROCESSORDATA LogicalProcessorMap[MAX_NUMBER_OF_LOGICAL_PROCESSORS] = {0};
int PhysProcIds[MAX_NUMBER_OF_PHYSICAL_PROCESSORS+MAX_NUMBER_OF_IOAPICS] = {0};
// Save the process affinity since it gets changed in cpuid()
DWORD process, system;
Bool rc = GetProcessAffinity(process, system);
if(rc)
{
nlp = QueryNumLogicalProcessors();
for ( i = 0; i < nlp; i++ )
cpuid( i, LogicalProcessorMap );
// Restore the saved process affinity because we like it when our games run on all the
// available cores!
rc = SetProcessAffinity(process);
if(rc)
{
num_processors = 0;
for ( i = 0; i < nlp; i++ )
PhysProcIds[LogicalProcessorMap[i].nProcId]++;
for ( i = 0; i < (MAX_NUMBER_OF_PHYSICAL_PROCESSORS+MAX_NUMBER_OF_IOAPICS); i++ )
if (PhysProcIds[i])
num_processors++;
*numberOfLogicalProcessors = nlp;
*numberOfCores = LogicalProcessorMap[0].nCPUCoresperProcessor * num_processors;
*numberOfPhysicalProcessors = num_processors;
}
}
return rc;
}
} // namespace Private
GearCPU::GearCPU()
{
FillInformation();
}
GearCPU::~GearCPU()
{
}
void GearCPU::FillInformation()
{
// The object cpuInfo will contain all the information we need to fill up the client structure
Private::CPUInfo cpuInfo;
///////////////////////////////////////////////////////////////////
// Processor name
///////////////////////////////////////////////////////////////////
if (cpuInfo.processorName[0] != 0)
{
m_name = cpuInfo.processorName;
}
else
{
m_name = "Unknown Processor";
}
///////////////////////////////////////////////////////////////////
// Frequency
// Here we basically query the registry to get the frequency computed
// by Windows, at boot time
///////////////////////////////////////////////////////////////////
clockFrequencyHz = 0;
HKEY hKey;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &hKey) == 0)
{
DWORD mhz, dwType;
DWORD bufSize = sizeof(mhz);
if (RegQueryValueExA(hKey, "~MHz", NULL, &dwType, (LPBYTE)&mhz, &bufSize) == 0)
{
clockFrequencyHz = mhz * 1000000;
}
RegCloseKey(hKey);
}
///////////////////////////////////////////////////////////////////
// CPU Vendor
// We retrieve the manufacturer ID from the vendor name
///////////////////////////////////////////////////////////////////
if (Str::StringCompare(cpuInfo.vendorName, "GenuineIntel") == 0)
vendor = CPU::VENDOR_INTEL;
else if (Str::StringCompare(cpuInfo.vendorName, "AuthenticAMD") == 0)
vendor = CPU::VENDOR_AMD;
else if (Str::StringCompare(cpuInfo.vendorName, "UMC UMC UMC ") == 0)
vendor = CPU::VENDOR_UMC;
else if (Str::StringCompare(cpuInfo.vendorName, "AMD ISBETTER") == 0)
vendor = CPU::VENDOR_AMD;
else if (Str::StringCompare(cpuInfo.vendorName, "CyrixInstead") == 0)
vendor = CPU::VENDOR_CYRIX;
else if (Str::StringCompare(cpuInfo.vendorName, "NexGenDriven") == 0)
vendor = CPU::VENDOR_NEXGEN;
else if (Str::StringCompare(cpuInfo.vendorName, "CentaurHauls") == 0)
vendor = CPU::VENDOR_CENTAUR;
else if (Str::StringCompare(cpuInfo.vendorName, "RiseRiseRise") == 0)
vendor = CPU::VENDOR_RISE;
else if (Str::StringCompare(cpuInfo.vendorName, "GenuineTMx86") == 0)
vendor = CPU::VENDOR_TRANSMETA;
else if (Str::StringCompare(cpuInfo.vendorName, "TransmetaCPU") == 0)
vendor = CPU::VENDOR_TRANSMETA;
else if (Str::StringCompare(cpuInfo.vendorName, "Geode By NSC") == 0)
vendor = CPU::VENDOR_NATIONALSEMICONDUCTOR;
else
vendor = CPU::VENDOR_UNKNOWN;
///////////////////////////////////////////////////////////////////
// The function CPUCount() computes the following parameters
// - nbHardwareThreads
// - nbCores
// - nbCPUs
///////////////////////////////////////////////////////////////////
if (vendor == CPU::VENDOR_AMD)
{
Bool rc = Private::AMDCPUCount(&nbHardwareThreads, &nbCores, &nbCPUs);
// If something failed, set everything to a safe value of 1
if(!rc)
nbHardwareThreads = nbCores = nbCPUs = 1;
}
else
{
Private::CPUCount(&nbHardwareThreads, &nbCores, &nbCPUs);
}
///////////////////////////////////////////////////////////////////
// Here we decode the feature flag values reported in ECX and EDX.
// We assume that each instruction set extends the previous version,
// i.e. a processor featuring SSE3 also features SSE2, SSE and MMX.
///////////////////////////////////////////////////////////////////
if (cpuInfo.function1.ecx.bits.SSE42 == 1)
supportedInstructionSet = CPU::INSTRUCTIONSET_SSE42;
else if (cpuInfo.function1.ecx.bits.SSE41 == 1)
supportedInstructionSet = CPU::INSTRUCTIONSET_SSE41;
else if (cpuInfo.function1.ecx.bits.SSSE3 == 1)
supportedInstructionSet = CPU::INSTRUCTIONSET_SSSE3;
else if (cpuInfo.function1.ecx.bits.SSE3 == 1)
supportedInstructionSet = CPU::INSTRUCTIONSET_SSE3;
else if (cpuInfo.function1.edx.bits.SSE2 == 1)
supportedInstructionSet = CPU::INSTRUCTIONSET_SSE2;
else if (cpuInfo.function1.edx.bits.SSE == 1)
supportedInstructionSet = CPU::INSTRUCTIONSET_SSE;
else if (cpuInfo.function1.edx.bits.MMX == 1)
supportedInstructionSet = CPU::INSTRUCTIONSET_MMX;
else
supportedInstructionSet = CPU::INSTRUCTIONSET_NONE;
}
const char* GearCPU::GetName() const
{
return m_name.CStr();
}
} //namespace SystemDetection
} // namespace G4()
//////////////////////////////////////////////////////////////////////////