495 lines
10 KiB
C++
495 lines
10 KiB
C++
/***
|
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: CpuInfo.cpp
|
|
Date: 2021-6-12
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "HWInfo.hpp"
|
|
#include "CpuInfo.hpp"
|
|
|
|
#if defined(AURORA_IS_BSD_DERIVED)
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#if defined(AURORA_IS_POSIX_DERIVED)
|
|
#include <stdlib.h>
|
|
#include <sys/sysinfo.h>
|
|
#endif
|
|
|
|
#if defined(AURORA_COMPILER_CLANG) || defined(AURORA_IS_POSIX_DERIVED)
|
|
#include <cpuid.h>
|
|
#endif
|
|
|
|
namespace Aurora::HWInfo
|
|
{
|
|
static CpuInfo gCpuInfo;
|
|
|
|
union CPUIdContext
|
|
{
|
|
struct
|
|
{
|
|
AuUInt32 eax;
|
|
AuUInt32 ebx;
|
|
AuUInt32 ecx;
|
|
AuUInt32 edx;
|
|
};
|
|
int regs[4];
|
|
};
|
|
|
|
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86)
|
|
#if defined(AURORA_COMPILER_MSVC)
|
|
static inline CPUIdContext cpuid(AuUInt32 a)
|
|
{
|
|
CPUIdContext context;
|
|
__cpuid(context.regs, a);
|
|
return context;
|
|
}
|
|
#elif defined(AURORA_COMPILER_CLANG) || defined(AURORA_COMPILER_GCC)
|
|
static inline CPUIdContext cpuid(AuUInt32 a)
|
|
{
|
|
CPUIdContext context;
|
|
__get_cpuid(a, &context.eax, &context.ebx, &context.ecx, &context.edx);
|
|
return context;
|
|
}
|
|
#else
|
|
static inline CPUIdContext cpuid(AuUInt32 a)
|
|
{
|
|
return {};
|
|
}
|
|
#endif
|
|
#else
|
|
static inline CPUIdContext cpuid(AuUInt32 a)
|
|
{
|
|
return {};
|
|
}
|
|
#endif
|
|
|
|
bool CpuId::SSE3()
|
|
{
|
|
return AuTestBit(f_1_ECX, 0);
|
|
}
|
|
|
|
bool CpuId::PCLMULQDQ()
|
|
{
|
|
return AuTestBit(f_1_ECX, 1);
|
|
}
|
|
|
|
bool CpuId::MONITOR()
|
|
{
|
|
return AuTestBit(f_1_ECX, 3);
|
|
}
|
|
|
|
bool CpuId::SSSE3()
|
|
{
|
|
return AuTestBit(f_1_ECX, 9);
|
|
}
|
|
|
|
bool CpuId::FMA()
|
|
{
|
|
return AuTestBit(f_1_ECX, 12);
|
|
}
|
|
|
|
bool CpuId::CMPXCHG16B()
|
|
{
|
|
return AuTestBit(f_1_ECX, 13);
|
|
}
|
|
|
|
bool CpuId::SSE41()
|
|
{
|
|
return AuTestBit(f_1_ECX, 19);
|
|
}
|
|
|
|
bool CpuId::SSE42()
|
|
{
|
|
return AuTestBit(f_1_ECX, 20);
|
|
}
|
|
|
|
bool CpuId::MOVBE()
|
|
{
|
|
return AuTestBit(f_1_ECX, 22);
|
|
}
|
|
|
|
bool CpuId::POPCNT()
|
|
{
|
|
return AuTestBit(f_1_ECX, 23);
|
|
}
|
|
|
|
bool CpuId::AES()
|
|
{
|
|
return AuTestBit(f_1_ECX, 25);
|
|
}
|
|
|
|
bool CpuId::XSAVE()
|
|
{
|
|
return AuTestBit(f_1_ECX, 26);
|
|
}
|
|
|
|
bool CpuId::OSXSAVE()
|
|
{
|
|
return AuTestBit(f_1_ECX, 27);
|
|
}
|
|
|
|
bool CpuId::AVX()
|
|
{
|
|
return AuTestBit(f_1_ECX, 28);
|
|
}
|
|
|
|
bool CpuId::F16C()
|
|
{
|
|
return AuTestBit(f_1_ECX, 29);
|
|
}
|
|
|
|
bool CpuId::RDRAND()
|
|
{
|
|
return AuTestBit(f_1_ECX, 30);
|
|
}
|
|
|
|
bool CpuId::MSR()
|
|
{
|
|
return AuTestBit(f_1_EDX, 5);
|
|
}
|
|
|
|
bool CpuId::CX8()
|
|
{
|
|
return AuTestBit(f_1_EDX, 8);
|
|
}
|
|
|
|
bool CpuId::SEP()
|
|
{
|
|
return AuTestBit(f_1_EDX, 11);
|
|
}
|
|
|
|
bool CpuId::CMOV()
|
|
{
|
|
return AuTestBit(f_1_EDX, 15);
|
|
}
|
|
|
|
bool CpuId::CLFSH()
|
|
{
|
|
return AuTestBit(f_1_EDX, 19);
|
|
}
|
|
|
|
bool CpuId::MMX()
|
|
{
|
|
return AuTestBit(f_1_EDX, 23);
|
|
}
|
|
|
|
bool CpuId::FXSR()
|
|
{
|
|
return AuTestBit(f_1_EDX, 24);
|
|
}
|
|
|
|
bool CpuId::SSE()
|
|
{
|
|
return AuTestBit(f_1_EDX, 25);
|
|
}
|
|
|
|
bool CpuId::SSE2()
|
|
{
|
|
return AuTestBit(f_1_EDX, 26);
|
|
}
|
|
|
|
bool CpuId::FSGSBASE()
|
|
{
|
|
return AuTestBit(f_7_EBX, 0);
|
|
}
|
|
|
|
bool CpuId::BMI1()
|
|
{
|
|
return AuTestBit(f_7_EBX, 3);
|
|
}
|
|
|
|
bool CpuId::HLE()
|
|
{
|
|
return isIntel && AuTestBit(f_7_EBX, 4);
|
|
}
|
|
|
|
bool CpuId::AVX2()
|
|
{
|
|
return AuTestBit(f_7_EBX, 5);
|
|
}
|
|
|
|
bool CpuId::BMI2()
|
|
{
|
|
return AuTestBit(f_7_EBX, 8);
|
|
}
|
|
|
|
bool CpuId::ERMS()
|
|
{
|
|
return AuTestBit(f_7_EBX, 9);
|
|
}
|
|
|
|
bool CpuId::INVPCID()
|
|
{
|
|
return AuTestBit(f_7_EBX, 10);
|
|
}
|
|
|
|
bool CpuId::RTM()
|
|
{
|
|
return isIntel && AuTestBit(f_7_EBX, 11);
|
|
}
|
|
|
|
bool CpuId::AVX512F()
|
|
{
|
|
return AuTestBit(f_7_EBX, 16);
|
|
}
|
|
|
|
bool CpuId::RDSEED()
|
|
{
|
|
return AuTestBit(f_7_EBX, 18);
|
|
}
|
|
|
|
bool CpuId::ADX()
|
|
{
|
|
return AuTestBit(f_7_EBX, 19);
|
|
}
|
|
|
|
bool CpuId::AVX512PF()
|
|
{
|
|
return AuTestBit(f_7_EBX, 26);
|
|
}
|
|
|
|
bool CpuId::AVX512ER()
|
|
{
|
|
return AuTestBit(f_7_EBX, 27);
|
|
}
|
|
|
|
bool CpuId::AVX512CD()
|
|
{
|
|
return AuTestBit(f_7_EBX, 28);
|
|
}
|
|
|
|
bool CpuId::SHA()
|
|
{
|
|
return AuTestBit(f_7_EBX, 29);
|
|
}
|
|
|
|
bool CpuId::PREFETCHWT1()
|
|
{
|
|
return AuTestBit(f_7_ECX, 0);
|
|
}
|
|
|
|
bool CpuId::LAHF()
|
|
{
|
|
return AuTestBit(f_81_ECX, 0);
|
|
}
|
|
|
|
bool CpuId::LZCNT()
|
|
{
|
|
return isIntel && AuTestBit(f_81_ECX, 5);
|
|
}
|
|
|
|
bool CpuId::ABM()
|
|
{
|
|
return isAMD && AuTestBit(f_81_ECX, 5);
|
|
}
|
|
|
|
bool CpuId::SSE4a()
|
|
{
|
|
return isAMD && AuTestBit(f_81_ECX, 6);
|
|
}
|
|
|
|
bool CpuId::XOP()
|
|
{
|
|
return isAMD && AuTestBit(f_81_ECX, 11);
|
|
}
|
|
|
|
bool CpuId::TBM()
|
|
{
|
|
return isAMD && AuTestBit(f_81_ECX, 21);
|
|
}
|
|
|
|
bool CpuId::SYSCALL()
|
|
{
|
|
return isIntel && AuTestBit(f_81_EDX, 11);
|
|
}
|
|
|
|
bool CpuId::MMXEXT()
|
|
{
|
|
return isAMD && AuTestBit(f_81_EDX, 22);
|
|
}
|
|
|
|
bool CpuId::RDTSCP()
|
|
{
|
|
return isIntel && AuTestBit(f_81_EDX, 27);
|
|
}
|
|
|
|
bool CpuId::_3DNOWEXT()
|
|
{
|
|
return isAMD && AuTestBit(f_81_EDX, 30);
|
|
}
|
|
|
|
bool CpuId::_3DNOW()
|
|
{
|
|
return isAMD && AuTestBit(f_81_EDX, 31);
|
|
}
|
|
|
|
AUKN_SYM const CpuInfo &GetCPUInfo()
|
|
{
|
|
return gCpuInfo;
|
|
}
|
|
|
|
static void SetCpuId()
|
|
{
|
|
// Credit: https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-160
|
|
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86)
|
|
std::vector<CPUIdContext> data;
|
|
std::vector<CPUIdContext> extdata;
|
|
|
|
auto cpuInfo = cpuid(0);
|
|
auto nIds = cpuInfo.eax;
|
|
|
|
for (int i = 0; i <= nIds; ++i)
|
|
{
|
|
data.push_back(cpuid(i));
|
|
}
|
|
|
|
char vendor[0x20];
|
|
memset(vendor, 0, sizeof(vendor));
|
|
*reinterpret_cast<int*>(vendor) = cpuInfo.ebx;
|
|
*reinterpret_cast<int*>(vendor + 4) = cpuInfo.edx;
|
|
*reinterpret_cast<int*>(vendor + 8) = cpuInfo.ecx;
|
|
|
|
gCpuInfo.cpuId.vendor = vendor;
|
|
|
|
if (gCpuInfo.cpuId.vendor == "GenuineIntel")
|
|
{
|
|
gCpuInfo.cpuId.isIntel = true;
|
|
}
|
|
else if (gCpuInfo.cpuId.vendor == "AuthenticAMD")
|
|
{
|
|
gCpuInfo.cpuId.isAMD = true;
|
|
}
|
|
|
|
// load bitset with flags for function 0x00000001
|
|
if (nIds >= 1)
|
|
{
|
|
gCpuInfo.cpuId.f_1_ECX = data[1].ecx;
|
|
gCpuInfo.cpuId.f_1_EDX = data[1].edx;
|
|
}
|
|
|
|
// load bitset with flags for function 0x00000007
|
|
if (nIds >= 7)
|
|
{
|
|
gCpuInfo.cpuId.f_7_EBX = data[7].ebx;
|
|
gCpuInfo.cpuId.f_7_ECX = data[7].ecx;
|
|
}
|
|
|
|
// gets the number of the highest valid extended ID.
|
|
auto cpui = cpuid(0x80000000);
|
|
auto nExIds = cpui.eax;
|
|
|
|
char brand[0x40];
|
|
memset(brand, 0, sizeof(brand));
|
|
|
|
for (int i = 0x80000000; i <= nExIds; ++i)
|
|
{
|
|
extdata.push_back(cpuid(i));
|
|
}
|
|
|
|
// load bitset with flags for function 0x80000001
|
|
if (nExIds >= 0x80000001)
|
|
{
|
|
gCpuInfo.cpuId.f_81_ECX = extdata[1].ecx;
|
|
gCpuInfo.cpuId.f_81_EDX = extdata[1].edx;
|
|
}
|
|
|
|
// Interpret CPU brand string if reported
|
|
if (nExIds >= 0x80000004)
|
|
{
|
|
memcpy(brand, &extdata[2], sizeof(cpui));
|
|
memcpy(brand + 16, &extdata[3], sizeof(cpui));
|
|
memcpy(brand + 32, &extdata[4], sizeof(cpui));
|
|
gCpuInfo.cpuId.brand = brand;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void SetCpuTopology()
|
|
{
|
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
|
SYSTEM_LOGICAL_PROCESSOR_INFORMATION sysinfo[128];
|
|
DWORD length = AuArraySize(sysinfo) * sizeof(*sysinfo);
|
|
|
|
if (!GetLogicalProcessorInformation(sysinfo, &length))
|
|
{
|
|
SYSTEM_INFO sysinfo;
|
|
GetSystemInfo(&sysinfo);
|
|
gCpuInfo.socket = 1;
|
|
gCpuInfo.cores = 1;
|
|
gCpuInfo.threads = sysinfo.dwNumberOfProcessors;
|
|
return;
|
|
}
|
|
|
|
length /= sizeof(*sysinfo);
|
|
|
|
gCpuInfo.socket = 0;
|
|
gCpuInfo.cores = 0;
|
|
gCpuInfo.threads = 0;
|
|
|
|
for (auto i = 0; i < length; i++)
|
|
{
|
|
if (sysinfo[i].Relationship == RelationProcessorCore)
|
|
{
|
|
gCpuInfo.cores++;
|
|
|
|
auto mask = sysinfo[i].ProcessorMask;
|
|
unsigned long offset {}, tmp;
|
|
while (offset != (sizeof(offset) * 8))
|
|
{
|
|
// Count the index to a 1
|
|
if (BitScanForward(&tmp, mask >> offset) == 0) break; // mask was zero, end of scan
|
|
offset += tmp;
|
|
|
|
// Count the 1's by inverting the bitmap and counting to 1
|
|
BitScanForward(&tmp, ~(mask >> offset));
|
|
offset += tmp;
|
|
|
|
// Increment threads by the bits set in
|
|
gCpuInfo.threads += tmp;
|
|
}
|
|
}
|
|
else if (sysinfo[i].Relationship == RelationProcessorPackage)
|
|
{
|
|
gCpuInfo.socket++;
|
|
}
|
|
}
|
|
|
|
#elif defined(AURORA_IS_BSD_DERIVED)
|
|
|
|
auto opt = QueryBsdHwStat(HW_AVAILCPU);
|
|
if (opt.value_or(0) < 1)
|
|
{
|
|
opt = QueryBsdHwStat(HW_NCPU);
|
|
}
|
|
|
|
gCpuInfo.socket = 1;
|
|
gCpuInfo.cores = 1;
|
|
gCpuInfo.threads = opt.value_or(1);
|
|
|
|
#elif defined(AURORA_IS_LINUX_DERIVED)
|
|
|
|
gCpuInfo.socket = 1;
|
|
gCpuInfo.cores = 1;
|
|
gCpuInfo.threads = get_nprocs();
|
|
|
|
#elif defined(AURORA_IS_POSIX_DERIVED)
|
|
|
|
gCpuInfo.socket = 1;
|
|
gCpuInfo.cores = 1;
|
|
gCpuInfo.threads = sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
#endif
|
|
}
|
|
|
|
void InitCpuInfo()
|
|
{
|
|
gCpuInfo.cpuArch = Aurora::Build::kCurrentArchitecture;
|
|
SetCpuId();
|
|
SetCpuTopology();
|
|
}
|
|
}
|