1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2024-06-22 21:13:15 +00:00

Merge branch 'update-cpufeatures' into next

This commit is contained in:
Carles Fernandez 2021-10-25 17:45:38 +02:00
commit 8849f78eb0
No known key found for this signature in database
GPG Key ID: 4C583C52B0C3877D
12 changed files with 373 additions and 198 deletions

View File

@ -270,10 +270,6 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
endif()
if(CMAKE_VERSION VERSION_GREATER 3.0 AND SUPPORTED_CPU_FEATURES_ARCH)
set(BUILD_PIC ON CACHE BOOL
"Build cpu_features with Position Independent Code (PIC)."
FORCE
)
set(USE_CPU_FEATURES ON)
set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}")
set(BUILD_SHARED_LIBS OFF)

View File

@ -32,13 +32,11 @@ option(BUILD_TESTING "Enable test (depends on googletest)." OFF)
# As a consequence it is discouraged to use cpu_features as a shared library because different compilers may interpret the code in different ways.
# Prefer static linking from source whenever possible.
option(BUILD_SHARED_LIBS "Build library as shared." OFF)
# PIC
option(BUILD_PIC "Build with Position Independant Code." OFF) # Default is off at least for GCC
# Force PIC on unix when building shared libs
# see: https://en.wikipedia.org/wiki/Position-independent_code
if(BUILD_SHARED_LIBS AND UNIX)
set(BUILD_PIC ON)
option(CMAKE_POSITION_INDEPENDENT_CODE "Build with Position Independant Code." ON)
endif()
include(CheckIncludeFile)
@ -110,7 +108,6 @@ add_library(utils OBJECT
${PROJECT_SOURCE_DIR}/src/stack_line_reader.c
${PROJECT_SOURCE_DIR}/src/string_view.c
)
set_property(TARGET utils PROPERTY POSITION_INDEPENDENT_CODE ${BUILD_PIC})
setup_include_and_definitions(utils)
#
@ -131,7 +128,6 @@ if(UNIX)
if(HAVE_STRONG_GETAUXVAL)
target_compile_definitions(unix_based_hardware_detection PRIVATE HAVE_STRONG_GETAUXVAL)
endif()
set_property(TARGET unix_based_hardware_detection PROPERTY POSITION_INDEPENDENT_CODE ${BUILD_PIC})
endif()
#
@ -148,7 +144,6 @@ add_library(cpu_features ${CPU_FEATURES_HDRS} ${CPU_FEATURES_SRCS})
set_target_properties(cpu_features PROPERTIES PUBLIC_HEADER "${CPU_FEATURES_HDRS}")
setup_include_and_definitions(cpu_features)
target_link_libraries(cpu_features PUBLIC ${CMAKE_DL_LIBS})
set_property(TARGET cpu_features PROPERTY POSITION_INDEPENDENT_CODE ${BUILD_PIC})
target_include_directories(cpu_features
PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpu_features>
)
@ -237,6 +232,7 @@ install(TARGETS cpu_features list_cpu_features
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(EXPORT CpuFeaturesTargets
NAMESPACE CpuFeatures::

View File

@ -10,6 +10,11 @@
CPU_FEATURES_START_CPP_NAMESPACE
// CPUID Vendors
#define CPU_FEATURES_VENDOR_GENUINE_INTEL "GenuineIntel"
#define CPU_FEATURES_VENDOR_AUTHENTIC_AMD "AuthenticAMD"
#define CPU_FEATURES_VENDOR_HYGON_GENUINE "HygonGenuine"
// See https://en.wikipedia.org/wiki/CPUID for a list of x86 cpu features.
// The field names are based on the short name provided in the wikipedia tables.
typedef struct

View File

@ -87,7 +87,8 @@ void CpuFeatures_StringView_CopyString(const StringView src, char* dst,
// Checks if line contains the specified whitespace separated word.
bool CpuFeatures_StringView_HasWord(const StringView line,
const char* const word);
const char* const word,
const char separator);
// Get key/value from line. key and value are separated by ": ".
// key and value are cleaned up from leading and trailing whitespaces.

View File

@ -107,8 +107,8 @@ static bool HandleAarch64Line(const LineResult result,
{
for (size_t i = 0; i < AARCH64_LAST_; ++i)
{
kSetters[i](&info->features,
CpuFeatures_StringView_HasWord(value, kCpuInfoFlags[i]));
kSetters[i](&info->features, CpuFeatures_StringView_HasWord(
value, kCpuInfoFlags[i], ' '));
}
}
else if (CpuFeatures_StringView_IsEquals(key, str("CPU implementer")))

View File

@ -71,8 +71,8 @@ static bool HandleArmLine(const LineResult result, ArmInfo* const info,
{
for (size_t i = 0; i < ARM_LAST_; ++i)
{
kSetters[i](&info->features,
CpuFeatures_StringView_HasWord(value, kCpuInfoFlags[i]));
kSetters[i](&info->features, CpuFeatures_StringView_HasWord(
value, kCpuInfoFlags[i], ' '));
}
}
else if (CpuFeatures_StringView_IsEquals(key, str("CPU implementer")))

View File

@ -28,8 +28,8 @@ static bool HandleMipsLine(const LineResult result,
{
for (size_t i = 0; i < MIPS_LAST_; ++i)
{
kSetters[i](features,
CpuFeatures_StringView_HasWord(value, kCpuInfoFlags[i]));
kSetters[i](features, CpuFeatures_StringView_HasWord(
value, kCpuInfoFlags[i], ' '));
}
}
}

View File

@ -71,7 +71,7 @@ static bool HandlePPCLine(const LineResult result,
StringView key, value;
if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value))
{
if (CpuFeatures_StringView_HasWord(key, "platform"))
if (CpuFeatures_StringView_HasWord(key, "platform", ' '))
{
CpuFeatures_StringView_CopyString(value, strings->platform,
sizeof(strings->platform));

View File

@ -88,7 +88,6 @@
defined(CPU_FEATURES_OS_FREEBSD)
#include "internal/filesystem.h" // Needed to parse /proc/cpuinfo
#include "internal/stack_line_reader.h" // Needed to parse /proc/cpuinfo
#include "internal/string_view.h" // Needed to parse /proc/cpuinfo
#elif defined(CPU_FEATURES_OS_DARWIN)
#if !defined(HAVE_SYSCTLBYNAME)
#error "Darwin needs support for sysctlbyname"
@ -98,6 +97,8 @@
#error "Unsupported OS"
#endif // CPU_FEATURES_OS
#include "internal/string_view.h"
////////////////////////////////////////////////////////////////////////////////
// Definitions for CpuId and GetXCR0Eax.
////////////////////////////////////////////////////////////////////////////////
@ -267,6 +268,11 @@ static int IsVendor(const Leaf leaf, const char* const name)
return leaf.ebx == ebx && leaf.ecx == ecx && leaf.edx == edx;
}
static int IsVendorByX86Info(const X86Info* info, const char* const name)
{
return memcmp(info->vendor, name, sizeof(info->vendor)) == 0;
}
static const CacheLevelInfo kEmptyCacheLevelInfo;
static CacheLevelInfo GetCacheLevelInfo(const uint32_t reg)
@ -1129,55 +1135,52 @@ static CacheLevelInfo GetCacheLevelInfo(const uint32_t reg)
}
}
static void GetByteArrayFromRegister(uint32_t result[4], const uint32_t reg)
{
for (int i = 0; i < 4; ++i)
{
result[i] = ExtractBitRange(reg, (i + 1) * 8, i * 8);
}
}
// From https://www.felixcloutier.com/x86/cpuid#tbl-3-12
static void ParseLeaf2(const int max_cpuid_leaf, CacheInfo* info)
{
Leaf leaf = SafeCpuId(max_cpuid_leaf, 2);
uint32_t registers[] = {leaf.eax, leaf.ebx, leaf.ecx, leaf.edx};
for (int i = 0; i < 4; ++i)
// The least-significant byte in register EAX (register AL) will always return
// 01H. Software should ignore this value and not interpret it as an
// informational descriptor.
leaf.eax &= 0xFFFFFF00; // Zeroing out AL. 0 is the empty descriptor.
// The most significant bit (bit 31) of each register indicates whether the
// register contains valid information (set to 0) or is reserved (set to 1).
if (IsBitSet(leaf.eax, 31)) leaf.eax = 0;
if (IsBitSet(leaf.ebx, 31)) leaf.ebx = 0;
if (IsBitSet(leaf.ecx, 31)) leaf.ecx = 0;
if (IsBitSet(leaf.edx, 31)) leaf.edx = 0;
uint8_t data[16];
#if __STDC_VERSION__ >= 201112L
_Static_assert(sizeof(Leaf) == sizeof(data), "Leaf must be 16 bytes");
#endif
memcpy(&data, &leaf, sizeof(data));
for (size_t i = 0; i < sizeof(data); ++i)
{
if (registers[i] & (1U << 31))
{
continue; // register does not contains valid information
}
uint32_t bytes[4];
GetByteArrayFromRegister(bytes, registers[i]);
for (int j = 0; j < 4; ++j)
{
if (bytes[j] == 0xFF)
break; // leaf 4 should be used to fetch cache information
info->levels[info->size] = GetCacheLevelInfo(bytes[j]);
}
const uint8_t descriptor = data[i];
if (descriptor == 0) continue;
info->levels[info->size] = GetCacheLevelInfo(descriptor);
info->size++;
}
}
static void ParseLeaf4(const int max_cpuid_leaf, CacheInfo* info)
// For newer Intel CPUs uses "CPUID, eax=0x00000004".
// For newer AMD CPUs uses "CPUID, eax=0x8000001D"
static void ParseCacheInfo(const int max_cpuid_leaf, uint32_t leaf_id,
CacheInfo* info)
{
info->size = 0;
for (int cache_id = 0; cache_id < CPU_FEATURES_MAX_CACHE_LEVEL; cache_id++)
{
const Leaf leaf = SafeCpuIdEx(max_cpuid_leaf, 4, cache_id);
const Leaf leaf = SafeCpuIdEx(max_cpuid_leaf, leaf_id, cache_id);
CacheType cache_type = ExtractBitRange(leaf.eax, 4, 0);
if (cache_type == CPU_FEATURE_CACHE_NULL)
{
info->levels[cache_id] = kEmptyCacheLevelInfo;
continue;
}
if (cache_type == CPU_FEATURE_CACHE_NULL) continue;
int level = ExtractBitRange(leaf.eax, 7, 5);
int line_size = ExtractBitRange(leaf.ebx, 11, 0) + 1;
int partitioning = ExtractBitRange(leaf.ebx, 21, 12) + 1;
int ways = ExtractBitRange(leaf.ebx, 31, 22) + 1;
int tlb_entries = leaf.ecx + 1;
int cache_size = (ways * partitioning * line_size * (tlb_entries));
info->levels[cache_id] = (CacheLevelInfo){.level = level,
info->levels[info->size] = (CacheLevelInfo){.level = level,
.cache_type = cache_type,
.cache_size = cache_size,
.ways = ways,
@ -1371,41 +1374,33 @@ static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info,
{
StackLineReader reader;
StackLineReader_Initialize(&reader, fd);
for (;;)
for (bool stop = false; !stop;)
{
const LineResult result = StackLineReader_NextLine(&reader);
if (result.eof) stop = true;
const StringView line = result.line;
const bool is_feature =
CpuFeatures_StringView_StartsWith(line, str(" Features="));
const bool is_feature2 =
CpuFeatures_StringView_StartsWith(line, str(" Features2="));
if (is_feature || is_feature2)
{
// Lines of interests are of the following form:
// " Features=0x1783fbff<PSE36,MMX,FXSR,SSE,SSE2,HTT>"
// We replace '<', '>' and ',' with space so we can search by
// whitespace separated word.
// TODO: Fix CpuFeatures_StringView_HasWord to allow for different
// separators.
for (size_t i = 0; i < line.size; ++i)
{
char* c = (char*)(&(line.ptr[i]));
if (*c == '<' || *c == '>' || *c == ',') *c = ' ';
}
if (is_feature)
{
features->sse = CpuFeatures_StringView_HasWord(line, "SSE");
features->sse2 = CpuFeatures_StringView_HasWord(line, "SSE2");
}
if (is_feature2)
{
features->sse3 = CpuFeatures_StringView_HasWord(line, "SSE3");
features->ssse3 = CpuFeatures_StringView_HasWord(line, "SSSE3");
features->sse4_1 = CpuFeatures_StringView_HasWord(line, "SSE4.1");
features->sse4_2 = CpuFeatures_StringView_HasWord(line, "SSE4.2");
}
}
if (result.eof) break;
if (!CpuFeatures_StringView_StartsWith(line, str(" Features")))
continue;
// Lines of interests are of the following form:
// " Features=0x1783fbff<PSE36,MMX,FXSR,SSE,SSE2,HTT>"
// We first extract the comma separated values between angle brackets.
StringView csv = result.line;
int index = CpuFeatures_StringView_IndexOfChar(csv, '<');
if (index >= 0) csv = CpuFeatures_StringView_PopFront(csv, index + 1);
if (csv.size > 0 && CpuFeatures_StringView_Back(csv) == '>')
csv = CpuFeatures_StringView_PopBack(csv, 1);
if (CpuFeatures_StringView_HasWord(csv, "SSE", ','))
features->sse = true;
if (CpuFeatures_StringView_HasWord(csv, "SSE2", ','))
features->sse2 = true;
if (CpuFeatures_StringView_HasWord(csv, "SSE3", ','))
features->sse3 = true;
if (CpuFeatures_StringView_HasWord(csv, "SSSE3", ','))
features->ssse3 = true;
if (CpuFeatures_StringView_HasWord(csv, "SSE4.1", ','))
features->sse4_1 = true;
if (CpuFeatures_StringView_HasWord(csv, "SSE4.2", ','))
features->sse4_2 = true;
}
CpuFeatures_CloseFile(fd);
}
@ -1416,25 +1411,22 @@ static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info,
{
StackLineReader reader;
StackLineReader_Initialize(&reader, fd);
for (;;)
for (bool stop = false; !stop;)
{
const LineResult result = StackLineReader_NextLine(&reader);
if (result.eof) stop = true;
const StringView line = result.line;
StringView key, value;
if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value))
{
if (CpuFeatures_StringView_IsEquals(key, str("flags")))
{
features->sse = CpuFeatures_StringView_HasWord(value, "sse");
features->sse2 = CpuFeatures_StringView_HasWord(value, "sse2");
features->sse3 = CpuFeatures_StringView_HasWord(value, "sse3");
features->ssse3 = CpuFeatures_StringView_HasWord(value, "ssse3");
features->sse4_1 = CpuFeatures_StringView_HasWord(value, "sse4_1");
features->sse4_2 = CpuFeatures_StringView_HasWord(value, "sse4_2");
break;
}
}
if (result.eof) break;
if (!CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value))
continue;
if (!CpuFeatures_StringView_IsEquals(key, str("flags"))) continue;
features->sse = CpuFeatures_StringView_HasWord(value, "sse", ' ');
features->sse2 = CpuFeatures_StringView_HasWord(value, "sse2", ' ');
features->sse3 = CpuFeatures_StringView_HasWord(value, "sse3", ' ');
features->ssse3 = CpuFeatures_StringView_HasWord(value, "ssse3", ' ');
features->sse4_1 = CpuFeatures_StringView_HasWord(value, "sse4_1", ' ');
features->sse4_2 = CpuFeatures_StringView_HasWord(value, "sse4_2", ' ');
break;
}
CpuFeatures_CloseFile(fd);
}
@ -1450,11 +1442,15 @@ static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info,
// Reference
// https://en.wikipedia.org/wiki/CPUID#EAX=80000000h:_Get_Highest_Extended_Function_Implemented.
static Leaf GetLeafByIdAMD(uint32_t leaf_id)
{
uint32_t max_extended = CpuId(0x80000000).eax;
return SafeCpuId(max_extended, leaf_id);
}
static void ParseExtraAMDCpuId(X86Info* info, OsPreserves os_preserves)
{
const Leaf leaf_80000000 = CpuId(0x80000000);
const uint32_t max_extended_cpuid_leaf = leaf_80000000.eax;
const Leaf leaf_80000001 = SafeCpuId(max_extended_cpuid_leaf, 0x80000001);
const Leaf leaf_80000001 = GetLeafByIdAMD(0x80000001);
X86Features* const features = &info->features;
@ -1477,9 +1473,9 @@ X86Info GetX86Info(void)
{
X86Info info = kEmptyX86Info;
const Leaf leaf_0 = CpuId(0);
const bool is_intel = IsVendor(leaf_0, "GenuineIntel");
const bool is_amd = IsVendor(leaf_0, "AuthenticAMD");
const bool is_hygon = IsVendor(leaf_0, "HygonGenuine");
const bool is_intel = IsVendor(leaf_0, CPU_FEATURES_VENDOR_GENUINE_INTEL);
const bool is_amd = IsVendor(leaf_0, CPU_FEATURES_VENDOR_AUTHENTIC_AMD);
const bool is_hygon = IsVendor(leaf_0, CPU_FEATURES_VENDOR_HYGON_GENUINE);
SetVendor(leaf_0, info.vendor);
if (is_intel || is_amd || is_hygon)
{
@ -1498,11 +1494,25 @@ CacheInfo GetX86CacheInfo(void)
{
CacheInfo info = kEmptyCacheInfo;
const Leaf leaf_0 = CpuId(0);
const uint32_t max_cpuid_leaf = leaf_0.eax;
if (IsVendor(leaf_0, "GenuineIntel"))
if (IsVendor(leaf_0, CPU_FEATURES_VENDOR_GENUINE_INTEL))
{
ParseLeaf2(max_cpuid_leaf, &info);
ParseLeaf4(max_cpuid_leaf, &info);
info.size = 0;
ParseLeaf2(leaf_0.eax, &info);
ParseCacheInfo(leaf_0.eax, 4, &info);
}
else if (IsVendor(leaf_0, CPU_FEATURES_VENDOR_AUTHENTIC_AMD) ||
IsVendor(leaf_0, CPU_FEATURES_VENDOR_HYGON_GENUINE))
{
const uint32_t max_ext = CpuId(0x80000000).eax;
const uint32_t cpuid_ext = SafeCpuId(max_ext, 0x80000001).ecx;
// If CPUID Fn8000_0001_ECX[TopologyExtensions]==0
// then CPUID Fn8000_0001_E[D,C,B,A]X is reserved.
// https://www.amd.com/system/files/TechDocs/25481.pdf
if (IsBitSet(cpuid_ext, 22))
{
ParseCacheInfo(max_ext, 0x8000001D, &info);
}
}
return info;
}
@ -1511,7 +1521,7 @@ CacheInfo GetX86CacheInfo(void)
X86Microarchitecture GetX86Microarchitecture(const X86Info* info)
{
if (memcmp(info->vendor, "GenuineIntel", sizeof(info->vendor)) == 0)
if (IsVendorByX86Info(info, CPU_FEATURES_VENDOR_GENUINE_INTEL))
{
switch (CPUID(info->family, info->model))
{
@ -1616,7 +1626,7 @@ X86Microarchitecture GetX86Microarchitecture(const X86Info* info)
return X86_UNKNOWN;
}
}
if (memcmp(info->vendor, "AuthenticAMD", sizeof(info->vendor)) == 0)
if (IsVendorByX86Info(info, CPU_FEATURES_VENDOR_AUTHENTIC_AMD))
{
switch (CPUID(info->family, info->model))
{
@ -1726,7 +1736,7 @@ X86Microarchitecture GetX86Microarchitecture(const X86Info* info)
return X86_UNKNOWN;
}
}
if (memcmp(info->vendor, "HygonGenuine", sizeof(info->vendor)) == 0)
if (IsVendorByX86Info(info, CPU_FEATURES_VENDOR_HYGON_GENUINE))
{
switch (CPUID(info->family, info->model))
{
@ -1737,23 +1747,20 @@ X86Microarchitecture GetX86Microarchitecture(const X86Info* info)
return X86_UNKNOWN;
}
static void SetString(const uint32_t max_cpuid_ext_leaf, const uint32_t leaf_id,
char* buffer)
{
const Leaf leaf = SafeCpuId(max_cpuid_ext_leaf, leaf_id);
// We allow calling memcpy from SetString which is only called when requesting
// X86BrandString.
memcpy(buffer, &leaf, sizeof(Leaf));
}
void FillX86BrandString(char brand_string[49])
{
const Leaf leaf_ext_0 = CpuId(0x80000000);
const uint32_t max_cpuid_leaf_ext = leaf_ext_0.eax;
SetString(max_cpuid_leaf_ext, 0x80000002, brand_string);
SetString(max_cpuid_leaf_ext, 0x80000003, brand_string + 16);
SetString(max_cpuid_leaf_ext, 0x80000004, brand_string + 32);
brand_string[48] = '\0';
const Leaf leaves[3] = {
SafeCpuId(max_cpuid_leaf_ext, 0x80000002),
SafeCpuId(max_cpuid_leaf_ext, 0x80000003),
SafeCpuId(max_cpuid_leaf_ext, 0x80000004),
};
#if __STDC_VERSION__ >= 201112L
_Static_assert(sizeof(leaves) == 48, "Leaves must be packed");
#endif
CpuFeatures_StringView_CopyString(view((const char*)leaves, sizeof(leaves)),
brand_string, 49);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -158,7 +158,8 @@ void CpuFeatures_StringView_CopyString(const StringView src, char* dst,
}
bool CpuFeatures_StringView_HasWord(const StringView line,
const char* const word_str)
const char* const word_str,
const char separator)
{
const StringView word = str(word_str);
StringView remainder = line;
@ -176,9 +177,9 @@ bool CpuFeatures_StringView_HasWord(const StringView line,
const StringView after =
CpuFeatures_StringView_PopFront(line, index_of_word + word.size);
const bool valid_before =
before.size == 0 || CpuFeatures_StringView_Back(before) == ' ';
before.size == 0 || CpuFeatures_StringView_Back(before) == separator;
const bool valid_after =
after.size == 0 || CpuFeatures_StringView_Front(after) == ' ';
after.size == 0 || CpuFeatures_StringView_Front(after) == separator;
if (valid_before && valid_after) return true;
remainder =
CpuFeatures_StringView_PopFront(remainder, index_of_word + word.size);

View File

@ -76,26 +76,32 @@ private:
uint32_t xcr0_eax_;
};
FakeCpu* g_fake_cpu = nullptr;
static FakeCpu* g_fake_cpu_instance = nullptr;
static FakeCpu& cpu()
{
assert(g_fake_cpu_instance != nullptr);
return *g_fake_cpu_instance;
}
extern "C" Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx)
{
return g_fake_cpu->GetCpuidLeaf(leaf_id, ecx);
return cpu().GetCpuidLeaf(leaf_id, ecx);
}
extern "C" uint32_t GetXCR0Eax(void) { return g_fake_cpu->GetXCR0Eax(); }
extern "C" uint32_t GetXCR0Eax(void) { return cpu().GetXCR0Eax(); }
#if defined(CPU_FEATURES_OS_DARWIN)
extern "C" bool GetDarwinSysCtlByName(const char* name)
{
return g_fake_cpu->GetDarwinSysCtlByName(name);
return cpu().GetDarwinSysCtlByName(name);
}
#endif // CPU_FEATURES_OS_DARWIN
#if defined(CPU_FEATURES_OS_WINDOWS)
extern "C" bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature)
{
return g_fake_cpu->GetWindowsIsProcessorFeaturePresent(ProcessorFeature);
return cpu().GetWindowsIsProcessorFeaturePresent(ProcessorFeature);
}
#endif // CPU_FEATURES_OS_WINDOWS
@ -104,14 +110,22 @@ namespace
class CpuidX86Test : public ::testing::Test
{
protected:
void SetUp() override { g_fake_cpu = new FakeCpu(); }
void TearDown() override { delete g_fake_cpu; }
void SetUp() override
{
assert(g_fake_cpu_instance == nullptr);
g_fake_cpu_instance = new FakeCpu();
}
void TearDown() override
{
delete g_fake_cpu_instance;
g_fake_cpu_instance = nullptr;
}
};
TEST_F(CpuidX86Test, SandyBridge)
{
g_fake_cpu->SetOsBackupsExtendedRegisters(true);
g_fake_cpu->SetLeaves({
cpu().SetOsBackupsExtendedRegisters(true);
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000206A6, 0x00100800, 0x1F9AE3BF, 0xBFEBFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
@ -153,28 +167,29 @@ TEST_F(CpuidX86Test, SandyBridge)
EXPECT_FALSE(features.adx);
}
const int UNDEF = -1;
const int KiB = 1024;
const int MiB = 1024 * KiB;
TEST_F(CpuidX86Test, SandyBridgeTestOsSupport)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000206A6, 0x00100800, 0x1F9AE3BF, 0xBFEBFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
});
// avx is disabled if os does not support backing up ymm registers.
g_fake_cpu->SetOsBackupsExtendedRegisters(false);
cpu().SetOsBackupsExtendedRegisters(false);
EXPECT_FALSE(GetX86Info().features.avx);
// avx is disabled if os does not support backing up ymm registers.
g_fake_cpu->SetOsBackupsExtendedRegisters(true);
cpu().SetOsBackupsExtendedRegisters(true);
EXPECT_TRUE(GetX86Info().features.avx);
}
TEST_F(CpuidX86Test, SkyLake)
{
g_fake_cpu->SetOsBackupsExtendedRegisters(true);
g_fake_cpu->SetLeaves({
cpu().SetOsBackupsExtendedRegisters(true);
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
@ -189,7 +204,7 @@ TEST_F(CpuidX86Test, SkyLake)
TEST_F(CpuidX86Test, Branding)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
@ -206,7 +221,7 @@ TEST_F(CpuidX86Test, Branding)
TEST_F(CpuidX86Test, KabyLakeCache)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
{{0x00000004, 0}, Leaf{0x1C004121, 0x01C0003F, 0x0000003F, 0x00000000}},
@ -256,7 +271,7 @@ TEST_F(CpuidX86Test, KabyLakeCache)
TEST_F(CpuidX86Test, HSWCache)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
{{0x00000004, 0}, Leaf{0x1C004121, 0x01C0003F, 0x0000003F, 0x00000000}},
@ -307,7 +322,7 @@ TEST_F(CpuidX86Test, HSWCache)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0200F30_K11_Griffin_CPUID.txt
TEST_F(CpuidX86Test, AMD_K11_GRIFFIN)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000001, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00200F30, 0x00020800, 0x00002001, 0x178BFBFF}},
{{0x80000000, 0}, Leaf{0x8000001A, 0x68747541, 0x444D4163, 0x69746E65}},
@ -324,7 +339,7 @@ TEST_F(CpuidX86Test, AMD_K11_GRIFFIN)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0300F10_K12_Llano_CPUID.txt
TEST_F(CpuidX86Test, AMD_K12_LLANO)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00300F10, 0x00040800, 0x00802009, 0x178BFBFF}},
{{0x80000000, 0}, Leaf{0x8000001B, 0x68747541, 0x444D4163, 0x69746E65}},
@ -341,7 +356,7 @@ TEST_F(CpuidX86Test, AMD_K12_LLANO)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0500F01_K14_Bobcat_CPUID.txt
TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F01)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00500F01, 0x00020800, 0x00802209, 0x178BFBFF}},
{{0x80000000, 0}, Leaf{0x8000001B, 0x68747541, 0x444D4163, 0x69746E65}},
@ -358,7 +373,7 @@ TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F01)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0500F10_K14_Bobcat_CPUID.txt
TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F10)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00500F10, 0x00020800, 0x00802209, 0x178BFBFF}},
{{0x00000002, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
@ -383,7 +398,7 @@ TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F10)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0500F20_K14_Bobcat_CPUID.txt
TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F20)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00500F20, 0x00020800, 0x00802209, 0x178BFBFF}},
{{0x80000000, 0}, Leaf{0x8000001B, 0x68747541, 0x444D4163, 0x69746E65}},
@ -400,7 +415,7 @@ TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F20)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0670F00_K15_StoneyRidge_CPUID.txt
TEST_F(CpuidX86Test, AMD_K15_EXCAVATOR_STONEY_RIDGE)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00670F00, 0x00020800, 0x7ED8320B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x000001A9, 0x00000000, 0x00000000}},
@ -426,7 +441,7 @@ TEST_F(CpuidX86Test, AMD_K15_EXCAVATOR_STONEY_RIDGE)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0600F20_K15_AbuDhabi_CPUID0.txt
TEST_F(CpuidX86Test, AMD_K15_PILEDRIVER_ABU_DHABI)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00600F20, 0x00100800, 0x3E98320B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x00000008, 0x00000000, 0x00000000}},
@ -449,10 +464,59 @@ TEST_F(CpuidX86Test, AMD_K15_PILEDRIVER_ABU_DHABI)
EXPECT_STREQ(brand_string, "AMD Opteron(tm) Processor 6376 ");
}
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0600F20_K15_AbuDhabi_CPUID0.txt
TEST_F(CpuidX86Test, AMD_K15_PILEDRIVER_ABU_DHABI_CACHE_INFO)
{
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00600F20, 0x00100800, 0x3E98320B, 0x178BFBFF}},
{{0x80000000, 0}, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x80000001, 0}, Leaf{0x00600F20, 0x30000000, 0x01EBBFFF, 0x2FD3FBFF}},
{{0x8000001D, 0}, Leaf{0x00000121, 0x00C0003F, 0x0000003F, 0x00000000}},
{{0x8000001D, 1}, Leaf{0x00004122, 0x0040003F, 0x000001FF, 0x00000000}},
{{0x8000001D, 2}, Leaf{0x00004143, 0x03C0003F, 0x000007FF, 0x00000001}},
{{0x8000001D, 3}, Leaf{0x0001C163, 0x0BC0003F, 0x000007FF, 0x00000001}},
});
const auto info = GetX86CacheInfo();
EXPECT_EQ(info.size, 4);
EXPECT_EQ(info.levels[0].level, 1);
EXPECT_EQ(info.levels[0].cache_type, 1);
EXPECT_EQ(info.levels[0].cache_size, 16 * KiB);
EXPECT_EQ(info.levels[0].ways, 4);
EXPECT_EQ(info.levels[0].line_size, 64);
EXPECT_EQ(info.levels[0].tlb_entries, 64);
EXPECT_EQ(info.levels[0].partitioning, 1);
EXPECT_EQ(info.levels[1].level, 1);
EXPECT_EQ(info.levels[1].cache_type, 2);
EXPECT_EQ(info.levels[1].cache_size, 64 * KiB);
EXPECT_EQ(info.levels[1].ways, 2);
EXPECT_EQ(info.levels[1].line_size, 64);
EXPECT_EQ(info.levels[1].tlb_entries, 512);
EXPECT_EQ(info.levels[1].partitioning, 1);
EXPECT_EQ(info.levels[2].level, 2);
EXPECT_EQ(info.levels[2].cache_type, 3);
EXPECT_EQ(info.levels[2].cache_size, 2 * MiB);
EXPECT_EQ(info.levels[2].ways, 16);
EXPECT_EQ(info.levels[2].line_size, 64);
EXPECT_EQ(info.levels[2].tlb_entries, 2048);
EXPECT_EQ(info.levels[2].partitioning, 1);
EXPECT_EQ(info.levels[3].level, 3);
EXPECT_EQ(info.levels[3].cache_type, 3);
EXPECT_EQ(info.levels[3].cache_size, 6 * MiB);
EXPECT_EQ(info.levels[3].ways, 48);
EXPECT_EQ(info.levels[3].line_size, 64);
EXPECT_EQ(info.levels[3].tlb_entries, 2048);
EXPECT_EQ(info.levels[3].partitioning, 1);
}
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0600F12_K15_Interlagos_CPUID3.txt
TEST_F(CpuidX86Test, AMD_K15_BULLDOZER_INTERLAGOS)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00600F12, 0x000C0800, 0x1E98220B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
@ -478,7 +542,7 @@ TEST_F(CpuidX86Test, AMD_K15_BULLDOZER_INTERLAGOS)
// http://users.atw.hu/instlatx64/AuthenticAMD0630F81_K15_Godavari_CPUID.txt
TEST_F(CpuidX86Test, AMD_K15_STREAMROLLER_GODAVARI)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00630F81, 0x00040800, 0x3E98320B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
@ -506,7 +570,7 @@ TEST_F(CpuidX86Test, AMD_K15_STREAMROLLER_GODAVARI)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0700F01_K16_Kabini_CPUID.txt
TEST_F(CpuidX86Test, AMD_K16_JAGUAR_KABINI)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00700F01, 0x00040800, 0x3ED8220B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x00000008, 0x00000000, 0x00000000}},
@ -531,7 +595,7 @@ TEST_F(CpuidX86Test, AMD_K16_JAGUAR_KABINI)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0730F01_K16_Beema_CPUID2.txt
TEST_F(CpuidX86Test, AMD_K16_PUMA_BEEMA)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00730F01, 0x00040800, 0x7ED8220B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x00000008, 0x00000000, 0x00000000}},
@ -556,7 +620,7 @@ TEST_F(CpuidX86Test, AMD_K16_PUMA_BEEMA)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0820F01_K17_Dali_CPUID.txt
TEST_F(CpuidX86Test, AMD_K17_ZEN_DALI)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00820F01, 0x00020800, 0x7ED8320B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x209C01A9, 0x00000000, 0x00000000}},
@ -581,7 +645,7 @@ TEST_F(CpuidX86Test, AMD_K17_ZEN_DALI)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0800F82_K17_ZenP_CPUID.txt
TEST_F(CpuidX86Test, AMD_K17_ZEN_PLUS_PINNACLE_RIDGE)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00800F82, 0x00100800, 0x7ED8320B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x209C01A9, 0x00000000, 0x00000000}},
@ -606,7 +670,7 @@ TEST_F(CpuidX86Test, AMD_K17_ZEN_PLUS_PINNACLE_RIDGE)
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0840F70_K17_CPUID.txt
TEST_F(CpuidX86Test, AMD_K17_ZEN2_XBOX_SERIES_X)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000010, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00840F70, 0x00100800, 0x7ED8320B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x219C91A9, 0x00400004, 0x00000000}},
@ -631,7 +695,7 @@ TEST_F(CpuidX86Test, AMD_K17_ZEN2_XBOX_SERIES_X)
// http://users.atw.hu/instlatx64/HygonGenuine/HygonGenuine0900F02_Hygon_CPUID3.txt
TEST_F(CpuidX86Test, AMD_K18_ZEN_DHYANA)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x6F677948, 0x656E6975, 0x6E65476E}},
{{0x00000001, 0}, Leaf{0x00900F02, 0x00100800, 0x74D83209, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x009C01A9, 0x0040068C, 0x00000000}},
@ -653,10 +717,59 @@ TEST_F(CpuidX86Test, AMD_K18_ZEN_DHYANA)
EXPECT_STREQ(brand_string, "Hygon C86 3185 8-core Processor ");
}
// http://users.atw.hu/instlatx64/HygonGenuine/HygonGenuine0900F02_Hygon_CPUID.txt
TEST_F(CpuidX86Test, AMD_K18_ZEN_DHYANA_CACHE_INFO)
{
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x6F677948, 0x656E6975, 0x6E65476E}},
{{0x00000001, 0}, Leaf{0x00900F02, 0x00100800, 0x74D83209, 0x178BFBFF}},
{{0x80000000, 0}, Leaf{0x8000001F, 0x6F677948, 0x656E6975, 0x6E65476E}},
{{0x80000001, 0}, Leaf{0x00900F02, 0x60000000, 0x35C233FF, 0x2FD3FBFF}},
{{0x8000001D, 0}, Leaf{0x00004121, 0x01C0003F, 0x0000003F, 0x00000000}},
{{0x8000001D, 1}, Leaf{0x00004122, 0x00C0003F, 0x000000FF, 0x00000000}},
{{0x8000001D, 2}, Leaf{0x00004143, 0x01C0003F, 0x000003FF, 0x00000002}},
{{0x8000001D, 3}, Leaf{0x0001C163, 0x03C0003F, 0x00001FFF, 0x00000001}},
});
const auto info = GetX86CacheInfo();
EXPECT_EQ(info.size, 4);
EXPECT_EQ(info.levels[0].level, 1);
EXPECT_EQ(info.levels[0].cache_type, 1);
EXPECT_EQ(info.levels[0].cache_size, 32 * KiB);
EXPECT_EQ(info.levels[0].ways, 8);
EXPECT_EQ(info.levels[0].line_size, 64);
EXPECT_EQ(info.levels[0].tlb_entries, 64);
EXPECT_EQ(info.levels[0].partitioning, 1);
EXPECT_EQ(info.levels[1].level, 1);
EXPECT_EQ(info.levels[1].cache_type, 2);
EXPECT_EQ(info.levels[1].cache_size, 64 * KiB);
EXPECT_EQ(info.levels[1].ways, 4);
EXPECT_EQ(info.levels[1].line_size, 64);
EXPECT_EQ(info.levels[1].tlb_entries, 256);
EXPECT_EQ(info.levels[1].partitioning, 1);
EXPECT_EQ(info.levels[2].level, 2);
EXPECT_EQ(info.levels[2].cache_type, 3);
EXPECT_EQ(info.levels[2].cache_size, 512 * KiB);
EXPECT_EQ(info.levels[2].ways, 8);
EXPECT_EQ(info.levels[2].line_size, 64);
EXPECT_EQ(info.levels[2].tlb_entries, 1024);
EXPECT_EQ(info.levels[2].partitioning, 1);
EXPECT_EQ(info.levels[3].level, 3);
EXPECT_EQ(info.levels[3].cache_type, 3);
EXPECT_EQ(info.levels[3].cache_size, 8 * MiB);
EXPECT_EQ(info.levels[3].ways, 16);
EXPECT_EQ(info.levels[3].line_size, 64);
EXPECT_EQ(info.levels[3].tlb_entries, 8192);
EXPECT_EQ(info.levels[3].partitioning, 1);
}
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0A20F10_K19_Vermeer2_CPUID.txt
TEST_F(CpuidX86Test, AMD_K19_ZEN3_VERMEER)
{
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000010, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00A20F10, 0x01180800, 0x7ED8320B, 0x178BFBFF}},
{{0x00000007, 0}, Leaf{0x00000000, 0x219C97A9, 0x0040068C, 0x00000000}},
@ -682,21 +795,18 @@ TEST_F(CpuidX86Test, AMD_K19_ZEN3_VERMEER)
TEST_F(CpuidX86Test, Nehalem)
{
// Pre AVX cpus don't have xsave
g_fake_cpu->SetOsBackupsExtendedRegisters(false);
cpu().SetOsBackupsExtendedRegisters(false);
#if defined(CPU_FEATURES_OS_WINDOWS)
g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
PF_XMMI_INSTRUCTIONS_AVAILABLE);
g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
PF_XMMI64_INSTRUCTIONS_AVAILABLE);
g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
PF_SSE3_INSTRUCTIONS_AVAILABLE);
cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
cpu().SetWindowsIsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE);
#elif defined(CPU_FEATURES_OS_DARWIN)
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse2");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse3");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.supplementalsse3");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_1");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_2");
cpu().SetDarwinSysCtlByName("hw.optional.sse");
cpu().SetDarwinSysCtlByName("hw.optional.sse2");
cpu().SetDarwinSysCtlByName("hw.optional.sse3");
cpu().SetDarwinSysCtlByName("hw.optional.supplementalsse3");
cpu().SetDarwinSysCtlByName("hw.optional.sse4_1");
cpu().SetDarwinSysCtlByName("hw.optional.sse4_2");
#elif defined(CPU_FEATURES_OS_FREEBSD)
auto& fs = GetEmptyFilesystem();
fs.CreateFile("/var/run/dmesg.boot", R"(
@ -713,7 +823,7 @@ real memory = 2147418112 (2047 MB)
flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
)");
#endif
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000106A2, 0x00100800, 0x00BCE3BD, 0xBFEBFBFF}},
{{0x00000002, 0}, Leaf{0x55035A01, 0x00F0B0E3, 0x00000000, 0x09CA212C}},
@ -768,21 +878,18 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
TEST_F(CpuidX86Test, Atom)
{
// Pre AVX cpus don't have xsave
g_fake_cpu->SetOsBackupsExtendedRegisters(false);
cpu().SetOsBackupsExtendedRegisters(false);
#if defined(CPU_FEATURES_OS_WINDOWS)
g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
PF_XMMI_INSTRUCTIONS_AVAILABLE);
g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
PF_XMMI64_INSTRUCTIONS_AVAILABLE);
g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
PF_SSE3_INSTRUCTIONS_AVAILABLE);
cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
cpu().SetWindowsIsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE);
#elif defined(CPU_FEATURES_OS_DARWIN)
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse2");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse3");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.supplementalsse3");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_1");
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_2");
cpu().SetDarwinSysCtlByName("hw.optional.sse");
cpu().SetDarwinSysCtlByName("hw.optional.sse2");
cpu().SetDarwinSysCtlByName("hw.optional.sse3");
cpu().SetDarwinSysCtlByName("hw.optional.supplementalsse3");
cpu().SetDarwinSysCtlByName("hw.optional.sse4_1");
cpu().SetDarwinSysCtlByName("hw.optional.sse4_2");
#elif defined(CPU_FEATURES_OS_FREEBSD)
auto& fs = GetEmptyFilesystem();
fs.CreateFile("/var/run/dmesg.boot", R"(
@ -799,7 +906,7 @@ real memory = 2147418112 (2047 MB)
flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
)");
#endif
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x00030673, 0x00100800, 0x41D8E3BF, 0xBFEBFBFF}},
{{0x00000002, 0}, Leaf{0x61B3A001, 0x0000FFC2, 0x00000000, 0x00000000}},
@ -850,16 +957,68 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
#endif // !defined(CPU_FEATURES_OS_WINDOWS)
}
// https://www.felixcloutier.com/x86/cpuid#example-3-1--example-of-cache-and-tlb-interpretation
TEST_F(CpuidX86Test, P4_CacheInfo)
{
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000002, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x00000F0A, 0x00010808, 0x00000000, 0x3FEBFBFF}},
{{0x00000002, 0}, Leaf{0x665B5001, 0x00000000, 0x00000000, 0x007A7000}},
});
const auto info = GetX86CacheInfo();
EXPECT_EQ(info.size, 5);
EXPECT_EQ(info.levels[0].level, UNDEF);
EXPECT_EQ(info.levels[0].cache_type, CPU_FEATURE_CACHE_TLB);
EXPECT_EQ(info.levels[0].cache_size, 4 * KiB);
EXPECT_EQ(info.levels[0].ways, UNDEF);
EXPECT_EQ(info.levels[0].line_size, UNDEF);
EXPECT_EQ(info.levels[0].tlb_entries, 64);
EXPECT_EQ(info.levels[0].partitioning, 0);
EXPECT_EQ(info.levels[1].level, UNDEF);
EXPECT_EQ(info.levels[1].cache_type, CPU_FEATURE_CACHE_TLB);
EXPECT_EQ(info.levels[1].cache_size, 4 * KiB);
EXPECT_EQ(info.levels[1].ways, UNDEF);
EXPECT_EQ(info.levels[1].line_size, UNDEF);
EXPECT_EQ(info.levels[1].tlb_entries, 64);
EXPECT_EQ(info.levels[1].partitioning, 0);
EXPECT_EQ(info.levels[2].level, 1);
EXPECT_EQ(info.levels[2].cache_type, CPU_FEATURE_CACHE_DATA);
EXPECT_EQ(info.levels[2].cache_size, 8 * KiB);
EXPECT_EQ(info.levels[2].ways, 4);
EXPECT_EQ(info.levels[2].line_size, 64);
EXPECT_EQ(info.levels[2].tlb_entries, UNDEF);
EXPECT_EQ(info.levels[2].partitioning, 0);
EXPECT_EQ(info.levels[3].level, 1);
EXPECT_EQ(info.levels[3].cache_type, CPU_FEATURE_CACHE_INSTRUCTION);
EXPECT_EQ(info.levels[3].cache_size, 12 * KiB);
EXPECT_EQ(info.levels[3].ways, 8);
EXPECT_EQ(info.levels[3].line_size, UNDEF);
EXPECT_EQ(info.levels[3].tlb_entries, UNDEF);
EXPECT_EQ(info.levels[3].partitioning, 0);
EXPECT_EQ(info.levels[4].level, 2);
EXPECT_EQ(info.levels[4].cache_type, CPU_FEATURE_CACHE_DATA);
EXPECT_EQ(info.levels[4].cache_size, 256 * KiB);
EXPECT_EQ(info.levels[4].ways, 8);
EXPECT_EQ(info.levels[4].line_size, 64);
EXPECT_EQ(info.levels[4].tlb_entries, UNDEF);
EXPECT_EQ(info.levels[4].partitioning, 2);
}
// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000673_P3_KatmaiDP_CPUID.txt
TEST_F(CpuidX86Test, P3)
{
// Pre AVX cpus don't have xsave
g_fake_cpu->SetOsBackupsExtendedRegisters(false);
cpu().SetOsBackupsExtendedRegisters(false);
#if defined(CPU_FEATURES_OS_WINDOWS)
g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
PF_XMMI_INSTRUCTIONS_AVAILABLE);
cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
#elif defined(CPU_FEATURES_OS_DARWIN)
g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
cpu().SetDarwinSysCtlByName("hw.optional.sse");
#elif defined(CPU_FEATURES_OS_FREEBSD)
auto& fs = GetEmptyFilesystem();
fs.CreateFile("/var/run/dmesg.boot", R"(
@ -875,7 +1034,7 @@ real memory = 2147418112 (2047 MB)
flags : fpu mmx sse
)");
#endif
g_fake_cpu->SetLeaves({
cpu().SetLeaves({
{{0x00000000, 0}, Leaf{0x00000003, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x00000673, 0x00000000, 0x00000000, 0x0387FBFF}},
{{0x00000002, 0}, Leaf{0x03020101, 0x00000000, 0x00000000, 0x0C040843}},

View File

@ -167,15 +167,25 @@ TEST(StringViewTest, CpuFeatures_StringView_HasWord)
{
// Find flags at beginning, middle and end.
EXPECT_TRUE(
CpuFeatures_StringView_HasWord(str("first middle last"), "first"));
CpuFeatures_StringView_HasWord(str("first middle last"), "first", ' '));
EXPECT_TRUE(
CpuFeatures_StringView_HasWord(str("first middle last"), "middle"));
EXPECT_TRUE(CpuFeatures_StringView_HasWord(str("first middle last"), "last"));
CpuFeatures_StringView_HasWord(str("first middle last"), "middle", ' '));
EXPECT_TRUE(
CpuFeatures_StringView_HasWord(str("first middle last"), "last", ' '));
// Find flags at beginning, middle and end with a different separator
EXPECT_TRUE(
CpuFeatures_StringView_HasWord(str("first-middle-last"), "first", '-'));
EXPECT_TRUE(
CpuFeatures_StringView_HasWord(str("first-middle-last"), "middle", '-'));
EXPECT_TRUE(
CpuFeatures_StringView_HasWord(str("first-middle-last"), "last", '-'));
// Do not match partial flags
EXPECT_FALSE(
CpuFeatures_StringView_HasWord(str("first middle last"), "irst"));
EXPECT_FALSE(CpuFeatures_StringView_HasWord(str("first middle last"), "mid"));
EXPECT_FALSE(CpuFeatures_StringView_HasWord(str("first middle last"), "las"));
CpuFeatures_StringView_HasWord(str("first middle last"), "irst", ' '));
EXPECT_FALSE(
CpuFeatures_StringView_HasWord(str("first middle last"), "mid", ' '));
EXPECT_FALSE(
CpuFeatures_StringView_HasWord(str("first middle last"), "las", ' '));
}
TEST(StringViewTest, CpuFeatures_StringView_GetAttributeKeyValue)