NazaraEngine/src/Nazara/Core/HardwareInfo.cpp

214 lines
8.5 KiB
C++

// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Core module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Core/HardwareInfo.hpp>
#include <Nazara/Core/Error.hpp>
#include <NazaraUtils/EnumMap.hpp>
#include <frozen/string.h>
#include <frozen/unordered_map.h>
#include <algorithm>
#include <cstring>
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Core/Win32/HardwareInfoImpl.hpp>
#elif defined(NAZARA_PLATFORM_POSIX)
#include <Nazara/Core/Posix/HardwareInfoImpl.hpp>
#else
#error Lack of implementation: HardwareInfo
#endif
#include <Nazara/Core/Debug.hpp>
namespace Nz
{
namespace NAZARA_ANONYMOUS_NAMESPACE
{
constexpr EnumMap<ProcessorVendor, std::string_view> s_vendorNames {
"ACRN", // ProcessorVendor::ACRN
"Advanced Micro Devices", // ProcessorVendor::AMD
"ao486", // ProcessorVendor::Ao486
"Apple Rosetta 2", // ProcessorVendor::AppleRosetta2
"bhyve", // ProcessorVendor::Bhyve
"Centaur Technology", // ProcessorVendor::Centaur
"Cyrix Corporation", // ProcessorVendor::Cyrix
"MCST Elbrus", // ProcessorVendor::Elbrus
"Hygon", // ProcessorVendor::Hygon
"Microsoft Hyper-V", // ProcessorVendor::HyperV
"Intel Corporation", // ProcessorVendor::Intel
"Kernel-based Virtual Machine", // ProcessorVendor::KVM
"Microsoft x86-to-ARM", // ProcessorVendor::MicrosoftXTA
"National Semiconductor", // ProcessorVendor::NSC
"NexGen", // ProcessorVendor::NexGen
"Parallels", // ProcessorVendor::Parallels
"QEMU", // ProcessorVendor::QEMU
"QNX Hypervisor", // ProcessorVendor::QNX
"Rise Technology", // ProcessorVendor::Rise
"Silicon Integrated Systems", // ProcessorVendor::SiS
"Transmeta Corporation", // ProcessorVendor::Transmeta
"United Microelectronics Corporation", // ProcessorVendor::UMC
"VIA Technologies", // ProcessorVendor::VIA
"VMware", // ProcessorVendor::VMware
"Vortex86", // ProcessorVendor::Vortex
"Xen", // ProcessorVendor::XenHVM
"Zhaoxin" // ProcessorVendor::Zhaoxin
};
static_assert(s_vendorNames.size() == ProcessorVendorCount, "Processor vendor name array is incomplete");
struct VendorString
{
char vendor[13]; // +1 for the end of string character
ProcessorVendor vendorEnum;
};
constexpr frozen::unordered_map s_vendorStrings = frozen::make_unordered_map<frozen::string, ProcessorVendor>({
{ " Shanghai ", ProcessorVendor::Zhaoxin },
{ " KVMKVMKVM ", ProcessorVendor::KVM },
{ " QNXQVMBSQG ", ProcessorVendor::QNX },
{ " lrpepyh vr", ProcessorVendor::Parallels },
{ "ACRNACRNACRN", ProcessorVendor::ACRN },
{ "AMDisbetter!", ProcessorVendor::AMD },
{ "AuthenticAMD", ProcessorVendor::AMD },
{ "CentaurHauls", ProcessorVendor::Centaur },
{ "CyrixInstead", ProcessorVendor::Cyrix },
{ "E2K MACHINE", ProcessorVendor::Elbrus },
{ "GenuineIntel", ProcessorVendor::Intel },
{ "GenuineTMx86", ProcessorVendor::Transmeta },
{ "Geode by NSC", ProcessorVendor::NSC },
{ "HygonGenuine", ProcessorVendor::Hygon },
{ "KVMKVMKVMKVM", ProcessorVendor::KVM },
{ "Microsoft Hv", ProcessorVendor::HyperV },
{ "MicrosoftXTA", ProcessorVendor::MicrosoftXTA },
{ "NexGenDriven", ProcessorVendor::NexGen },
{ "RiseRiseRise", ProcessorVendor::Rise },
{ "SiS SiS SiS ", ProcessorVendor::SIS },
{ "TCGTCGTCGTCG", ProcessorVendor::QEMU },
{ "TransmetaCPU", ProcessorVendor::Transmeta },
{ "UMC UMC UMC ", ProcessorVendor::UMC },
{ "VIA VIA VIA ", ProcessorVendor::VIA },
{ "VMwareVMware", ProcessorVendor::VMware },
{ "VirtualApple", ProcessorVendor::AppleRosetta2 },
{ "Vortex86 SoC", ProcessorVendor::Vortex },
{ "XenVMMXenVMM", ProcessorVendor::XenHVM },
{ "bhyve bhyve ", ProcessorVendor::Bhyve },
{ "prl hyperv ", ProcessorVendor::Parallels }, //< endianness-fixed version of " lrpepyh vr"
});
}
/*!
* \ingroup core
* \class Nz::HardwareInfo
* \brief Core class that represents the info we can get from hardware
*/
HardwareInfo::HardwareInfo()
{
FetchCPUInfo();
FetchMemoryInfo();
}
std::string_view HardwareInfo::GetCpuVendorName() const
{
NAZARA_USE_ANONYMOUS_NAMESPACE
return s_vendorNames[m_cpuVendor];
}
void HardwareInfo::Cpuid(UInt32 functionId, UInt32 subFunctionId, UInt32 result[4])
{
return HardwareInfoImpl::Cpuid(functionId, subFunctionId, result);
}
bool HardwareInfo::IsCpuidSupported()
{
return HardwareInfoImpl::IsCpuidSupported();
}
void HardwareInfo::FetchCPUInfo()
{
NAZARA_USE_ANONYMOUS_NAMESPACE
m_cpuThreadCount = std::max(HardwareInfoImpl::GetProcessorCount(), 1U);
m_cpuCapabilities.fill(false);
m_cpuVendor = ProcessorVendor::Unknown;
std::strcpy(m_cpuBrandString.data(), "CPU from unknown vendor - cpuid not supported");
if (!HardwareInfoImpl::IsCpuidSupported())
return;
// To begin, we get the id of the constructor and the id of maximal functions supported by the CPUID
std::array<UInt32, 4> registers;
HardwareInfoImpl::Cpuid(0, 0, registers.data());
UInt32& eax = registers[0];
UInt32& ebx = registers[1];
UInt32& ecx = registers[2];
UInt32& edx = registers[3];
UInt32 manufacturerId[3] = { ebx, edx, ecx };
frozen::string manufactorID(reinterpret_cast<const char*>(&manufacturerId[0]), 12);
if (auto vendorIt = s_vendorStrings.find(manufactorID); vendorIt != s_vendorStrings.end())
m_cpuVendor = vendorIt->second;
else
m_cpuVendor = ProcessorVendor::Unknown;
if (eax >= 1)
{
// Retrieval of certain capacities of the processor (ECX and EDX, function 1)
HardwareInfoImpl::Cpuid(1, 0, registers.data());
m_cpuCapabilities[ProcessorCap::AES] = (ecx & (1U << 25)) != 0;
m_cpuCapabilities[ProcessorCap::AVX] = (ecx & (1U << 28)) != 0;
m_cpuCapabilities[ProcessorCap::FMA3] = (ecx & (1U << 12)) != 0;
m_cpuCapabilities[ProcessorCap::MMX] = (edx & (1U << 23)) != 0;
m_cpuCapabilities[ProcessorCap::Popcnt] = (ecx & (1U << 23)) != 0;
m_cpuCapabilities[ProcessorCap::RDRAND] = (ecx & (1U << 30)) != 0;
m_cpuCapabilities[ProcessorCap::SSE] = (edx & (1U << 25)) != 0;
m_cpuCapabilities[ProcessorCap::SSE2] = (edx & (1U << 26)) != 0;
m_cpuCapabilities[ProcessorCap::SSE3] = (ecx & (1U << 0)) != 0;
m_cpuCapabilities[ProcessorCap::SSSE3] = (ecx & (1U << 9)) != 0;
m_cpuCapabilities[ProcessorCap::SSE41] = (ecx & (1U << 19)) != 0;
m_cpuCapabilities[ProcessorCap::SSE42] = (ecx & (1U << 20)) != 0;
}
// Retrieval of biggest extended function handled (EAX, function 0x80000000)
HardwareInfoImpl::Cpuid(0x80000000, 0, registers.data());
UInt32 maxSupportedExtendedFunction = eax;
if (maxSupportedExtendedFunction >= 0x80000001)
{
// Retrieval of extended capabilities of the processor (ECX and EDX, function 0x80000001)
HardwareInfoImpl::Cpuid(0x80000001, 0, registers.data());
m_cpuCapabilities[ProcessorCap::x64] = (edx & (1U << 29)) != 0; // Support of 64bits, doesn't mean executable is 64bits
m_cpuCapabilities[ProcessorCap::FMA4] = (ecx & (1U << 16)) != 0;
m_cpuCapabilities[ProcessorCap::SSE4a] = (ecx & (1U << 6)) != 0;
m_cpuCapabilities[ProcessorCap::XOP] = (ecx & (1U << 11)) != 0;
if (maxSupportedExtendedFunction >= 0x80000004)
{
// Retrieval of the string describing the processor (EAX, EBX, ECX and EDX, functions from 0x80000002 to 0x80000004 inclusive)
char* ptr = &m_cpuBrandString[0];
for (UInt32 code = 0x80000002; code <= 0x80000004; ++code)
{
HardwareInfoImpl::Cpuid(code, 0, registers.data());
std::memcpy(ptr, &registers[0], 4*sizeof(UInt32)); // We add the 16 bytes to the string
ptr += 4 * sizeof(UInt32);
}
// The character '\0' is already part of the string
}
}
}
void HardwareInfo::FetchMemoryInfo()
{
m_systemTotalMemory = HardwareInfoImpl::GetTotalMemory();
}
}