blob: 8b1f427b71a1a177578188cb2a1f6353ffdfc128 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/arch/testing/x86/fake-cpuid.h>
#include <lib/arch/x86/apic-id.h>
#include <lib/arch/x86/cache.h>
#include <lib/arch/x86/cpuid.h>
#include <string_view>
#include <vector>
#include <gtest/gtest.h>
// This file is meant for testing lib/arch logic that deals in CpuidIo access,
// along with expressing expectations of the accessed values for the suite of
// particular processors included in the CPUID corpus (see
// //zircon/kernel/lib/arch/test/data/cpuid/README.md). Expectations on the
// full cross-product of (CpuidIo logic, corpus entry) should be found below.
namespace {
using namespace std::string_view_literals;
using arch::testing::X86Microprocessor;
void CheckApicIdDecoding(const arch::testing::FakeCpuidIo& cpuid, //
uint32_t dies_per_package, //
uint32_t cores_per_die, //
uint32_t threads_per_core) {
arch::ApicIdDecoder decoder(cpuid);
// We expect the maximum number of *addressible* dies per package to be
// be greater thans or equal to the actual count; ditto for cores
// per die, and threads per core. In general, the individual address
// spaces might be larger than needed.
EXPECT_LE(dies_per_package, decoder.die_id(0xffffffff) + 1);
EXPECT_LE(cores_per_die, decoder.core_id(0xffffffff) + 1);
EXPECT_LE(threads_per_core, decoder.smt_id(0xffffffff) + 1);
}
void CheckCaches(const arch::testing::FakeCpuidIo& cpuid,
const std::vector<arch::CpuCacheLevelInfo>& expected_caches) {
arch::CpuCacheInfo caches(cpuid);
ASSERT_EQ(expected_caches.size(), caches.size());
{
auto actual = caches.begin();
auto expected = expected_caches.cbegin();
while (actual != caches.end() && expected != expected_caches.cend()) {
EXPECT_EQ(expected->level, actual->level);
EXPECT_EQ(expected->type, actual->type);
EXPECT_EQ(expected->size_kb, actual->size_kb);
EXPECT_EQ(expected->ways_of_associativity, actual->ways_of_associativity);
++actual;
++expected;
}
}
// And also compare last-level caches.
{
const auto& actual_llc = caches.back();
const auto& expected_llc = expected_caches.back();
EXPECT_EQ(expected_llc.level, actual_llc.level);
EXPECT_EQ(expected_llc.type, actual_llc.type);
EXPECT_EQ(expected_llc.size_kb, actual_llc.size_kb);
EXPECT_EQ(expected_llc.ways_of_associativity, actual_llc.ways_of_associativity);
}
}
//
// Tests.
//
TEST(CpuidTests, IntelCore2_6300) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCore2_6300);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelCore2, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x0fu, info.model());
EXPECT_EQ(0x02u, info.stepping());
EXPECT_EQ("Intel(R) Core(TM)2 CPU 6300 @ 1.86GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.avx());
EXPECT_FALSE(features.osxsave());
EXPECT_FALSE(features.xsave());
EXPECT_FALSE(features.x2apic());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
EXPECT_FALSE(features.fsgsbase());
}
// 1 die -> 2 cores -> 1 thread each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 1));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 2048,
.ways_of_associativity = 8,
},
}));
}
TEST(CpuidTests, IntelXeonE5520) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelXeonE5520);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelNehalem, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x1au, info.model());
EXPECT_EQ(0x05u, info.stepping());
EXPECT_EQ("Intel(R) Xeon(R) CPU E5520 @ 2.27GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.avx());
EXPECT_FALSE(features.osxsave());
EXPECT_FALSE(features.xsave());
EXPECT_FALSE(features.x2apic());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
EXPECT_FALSE(features.fsgsbase());
}
// 1 die -> 4 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 4, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 4,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 8192,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, IntelCoreI7_2600k) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCoreI7_2600k);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelSandyBridge, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x2au, info.model());
EXPECT_EQ(0x07u, info.stepping());
EXPECT_EQ(" Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.x2apic());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
EXPECT_FALSE(features.fsgsbase());
}
// 1 die -> 4 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 4, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 8192,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, IntelCoreI3_3240) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCoreI3_3240);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelIvyBridge, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x3au, info.model());
EXPECT_EQ(0x09u, info.stepping());
EXPECT_EQ(" Intel(R) Core(TM) i3-3240 CPU @ 3.40GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.x2apic());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
}
// 1 die -> 2 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 3072,
.ways_of_associativity = 12,
},
}));
}
TEST(CpuidTests, IntelXeonE5_2690_V3) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelXeonE5_2690_V3);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelHaswell, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x3fu, info.model());
EXPECT_EQ(0x02u, info.stepping());
EXPECT_EQ("Intel(R) Xeon(R) CPU E5-2690 v3 @ 2.60GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.x2apic());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present.
EXPECT_FALSE(features.hypervisor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
}
// 1 die -> 12 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 12, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 30720,
.ways_of_associativity = 20,
},
}));
}
TEST(CpuidTests, IntelXeonE5_2690_V4) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelXeonE5_2690_V4);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelBroadwell, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x4fu, info.model());
EXPECT_EQ(0x01u, info.stepping());
EXPECT_EQ("Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.x2apic());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present.
EXPECT_FALSE(features.hypervisor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.fsgsbase());
EXPECT_TRUE(features.intel_pt());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
}
// 1 die -> 14 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 14, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 35840,
.ways_of_associativity = 20,
},
}));
}
TEST(CpuidTests, IntelCoreI3_6100) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCoreI3_6100);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelSkylake, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x4eu, info.model());
EXPECT_EQ(0x03u, info.stepping());
EXPECT_EQ("Intel(R) Core(TM) i3-6100U CPU @ 2.30GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.x2apic());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.intel_pt());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
}
// 1 die -> 2 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 4,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 3072,
.ways_of_associativity = 12,
},
}));
}
TEST(CpuidTests, IntelCoreI5_7300u) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCoreI5_7300u);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelSkylake, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x8eu, info.model());
EXPECT_EQ(0x09u, info.stepping());
EXPECT_EQ("Intel(R) Core(TM) i5-7300U CPU @ 2.60GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.x2apic());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.intel_pt());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
}
// 1 die -> 2 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 4,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 3072,
.ways_of_associativity = 12,
},
}));
}
TEST(CpuidTests, IntelCoreI7_6500u) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCoreI7_6500u);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelSkylake, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x4eu, info.model());
EXPECT_EQ(0x03u, info.stepping());
EXPECT_EQ("Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.x2apic());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.intel_pt());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
}
// 1 die -> 2 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 4,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 4096,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, IntelCoreI7_6700k) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCoreI7_6700k);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelSkylake, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x5eu, info.model());
EXPECT_EQ(0x03u, info.stepping());
EXPECT_EQ("Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.x2apic());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.intel_pt());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
}
// 1 die -> 4 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 4, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 4,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 8192,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, IntelCoreM3_7y30) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCoreM3_7y30);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelSkylake, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x8eu, info.model());
EXPECT_EQ(0x09u, info.stepping());
EXPECT_EQ("Intel(R) Core(TM) m3-7Y30 CPU @ 1.00GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.x2apic());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.intel_pt());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
}
// 1 die -> 2 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 4,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 4096,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, IntelAtom330) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelAtom330);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelBonnell, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x1cu, info.model());
EXPECT_EQ(0x2u, info.stepping());
EXPECT_EQ(" Intel(R) Atom(TM) CPU 330 @ 1.60GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.avx());
EXPECT_FALSE(features.osxsave());
EXPECT_FALSE(features.xsave());
EXPECT_FALSE(features.x2apic());
EXPECT_TRUE(features.monitor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
EXPECT_FALSE(features.fsgsbase());
}
// 1 die -> 2 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 24,
.ways_of_associativity = 6,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
}));
}
TEST(CpuidTests, IntelAtomD510) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelAtomD510);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelBonnell, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x1cu, info.model());
EXPECT_EQ(0x0au, info.stepping());
EXPECT_EQ(" Intel(R) Atom(TM) CPU D510 @ 1.66GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.avx());
EXPECT_FALSE(features.osxsave());
EXPECT_FALSE(features.xsave());
EXPECT_FALSE(features.x2apic());
EXPECT_TRUE(features.monitor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
EXPECT_FALSE(features.fsgsbase());
}
// 1 die -> 2 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 24,
.ways_of_associativity = 6,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
}));
}
TEST(CpuidTests, IntelAtomX5_Z8350) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelAtomX5_Z8350);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelAirmont, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x4cu, info.model());
EXPECT_EQ(0x4u, info.stepping());
EXPECT_EQ(" Intel(R) Atom(TM) x5-Z8350 CPU @ 1.44GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.avx());
EXPECT_FALSE(features.osxsave());
EXPECT_FALSE(features.xsave());
EXPECT_FALSE(features.x2apic());
EXPECT_TRUE(features.monitor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
EXPECT_FALSE(features.fsgsbase());
}
// 1 die -> 4 cores -> 1 thread each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 4, 1));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 24,
.ways_of_associativity = 6,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 1024,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, IntelCeleron3855u) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelCeleron3855u);
EXPECT_EQ(arch::Vendor::kIntel, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kIntelSkylake, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x6u, info.family());
EXPECT_EQ(0x4eu, info.model());
EXPECT_EQ(0x3u, info.stepping());
EXPECT_EQ("Intel(R) Celeron(R) CPU 3855U @ 1.60GHz"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.x2apic());
EXPECT_TRUE(features.pdcm());
EXPECT_TRUE(features.cmpxchg16b());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.avx());
EXPECT_TRUE(features.monitor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.intel_pt());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
}
// 1 die -> 2 cores -> 1 thread each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 1));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 256,
.ways_of_associativity = 4,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 2048,
.ways_of_associativity = 8,
},
}));
}
TEST(CpuidTests, AmdA10_7870k) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdA10_7870k);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x15, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x15u, info.family());
EXPECT_EQ(0x38u, info.model());
EXPECT_EQ(0x01u, info.stepping());
EXPECT_EQ("AMD A10-7870K Radeon R7, 12 Compute Cores 4C+8G"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
}
// 1 die -> 4 cores -> 1 thread each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 4, 1));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 16,
.ways_of_associativity = 4,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 96,
.ways_of_associativity = 3,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 2048,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, AmdRyzen5_1500x) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen5_1500x);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x01u, info.model());
EXPECT_EQ(0x01u, info.stepping());
EXPECT_EQ("AMD Ryzen 5 1500X Quad-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
}
// 1 die -> 4 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 4, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 64,
.ways_of_associativity = 4,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 8192,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, AmdRyzen7_1700) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen7_1700);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x01u, info.model());
EXPECT_EQ(0x01u, info.stepping());
EXPECT_EQ("AMD Ryzen 7 1700 Eight-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
}
// 1 die -> 8 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 8, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 64,
.ways_of_associativity = 4,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 16384, // Total L3 size.
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, AmdRyzen7_2700x) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen7_2700x);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x08u, info.model());
EXPECT_EQ(0x02u, info.stepping());
EXPECT_EQ("AMD Ryzen 7 2700X Eight-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
}
// 1 die -> 8 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 8, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 64,
.ways_of_associativity = 4,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 8192,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, AmdRyzen9_3950x) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen9_3950x);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x71u, info.model());
EXPECT_EQ(0x00u, info.stepping());
EXPECT_EQ("AMD Ryzen 9 3950X 16-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
}
// 1 die -> 16 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 16, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 16384,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, AmdRyzen9_3950xVirtualBoxHyperv) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen9_3950xVirtualBoxHyperv);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x71u, info.model());
EXPECT_EQ(0x00u, info.stepping());
EXPECT_EQ("AMD Ryzen 9 3950X 16-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_EQ("VBoxVBoxVBox"sv, arch::HypervisorName(cpuid).name());
EXPECT_EQ(0x4000'0006u, cpuid.Read<arch::CpuidMaximumHypervisorLeaf>().leaf());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.hypervisor());
// Not present:
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.avx());
EXPECT_FALSE(features.osxsave());
EXPECT_FALSE(features.xsave());
EXPECT_FALSE(features.cmpxchg16b());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
EXPECT_FALSE(features.monitor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
}
// Seems to emulate
// 1 die -> 1 core -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 1, 2));
// Topology leaves are reserved, so we expect to only be able to be surface
// the total L3 size across the package.
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 4 * 16384,
},
}));
}
TEST(CpuidTests, AmdRyzen9_3950xVirtualBoxKvm) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen9_3950xVirtualBoxKvm);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x71u, info.model());
EXPECT_EQ(0x00u, info.stepping());
EXPECT_EQ("AMD Ryzen 9 3950X 16-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_EQ("KVMKVMKVM"sv, arch::HypervisorName(cpuid).name());
EXPECT_EQ(0x4000'0001u, cpuid.Read<arch::CpuidMaximumHypervisorLeaf>().leaf());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.hypervisor());
// Not present:
EXPECT_FALSE(features.rdrand());
EXPECT_FALSE(features.avx());
EXPECT_FALSE(features.osxsave());
EXPECT_FALSE(features.xsave());
EXPECT_FALSE(features.cmpxchg16b());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
EXPECT_FALSE(features.monitor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
EXPECT_FALSE(features.smap());
EXPECT_FALSE(features.rdseed());
}
// Seems to emulate
// 1 die -> 1 core -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 1, 2));
// Topology leaves are reserved, so we expect to only be able to be surface
// the total L3 size across the package.
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 4 * 16384,
},
}));
}
TEST(CpuidTests, AmdRyzen9_3950xVmware) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen9_3950xVmware);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x71u, info.model());
EXPECT_EQ(0x00u, info.stepping());
EXPECT_EQ("AMD Ryzen 9 3950X 16-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_EQ("VMwareVMware"sv, arch::HypervisorName(cpuid).name());
EXPECT_EQ(0x4000'0010u, cpuid.Read<arch::CpuidMaximumHypervisorLeaf>().leaf());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.hypervisor());
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.x2apic());
// Not present:
EXPECT_FALSE(features.pdcm());
EXPECT_FALSE(features.monitor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_FALSE(features.intel_pt());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.fsgsbase());
}
// Seems to emulate
// 1 die -> 4 cores -> 1 thread each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 4, 1));
// Topology leaves are reserved, so we expect to only be able to be surface
// the total L3 size across the package.
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 4 * 16384,
},
}));
}
TEST(CpuidTests, AmdRyzen9_3950xWsl2) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen9_3950xWsl2);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x71u, info.model());
EXPECT_EQ(0x00u, info.stepping());
EXPECT_EQ("AMD Ryzen 9 3950X 16-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_EQ("Microsoft Hv"sv, arch::HypervisorName(cpuid).name());
EXPECT_EQ(0x4000'000bu, cpuid.Read<arch::CpuidMaximumHypervisorLeaf>().leaf());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.hypervisor());
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
// Not present:
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
EXPECT_FALSE(features.monitor());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
}
// Like the underlying hardware, seems to emulate
// 1 die -> 16 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 16, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 16384,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, AmdRyzenThreadripper1950x) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzenThreadripper1950x);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x01u, info.model());
EXPECT_EQ(0x01u, info.stepping());
EXPECT_EQ("AMD Ryzen Threadripper 1950X 16-Core Processor "sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
}
// 2 dies -> 8 cores each -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 2, 8, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 64,
.ways_of_associativity = 4,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 8192,
.ways_of_associativity = 16,
},
}));
}
TEST(CpuidTests, AmdRyzenThreadripper2970wx) {
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzenThreadripper2970wx);
EXPECT_EQ(arch::Vendor::kAmd, arch::GetVendor(cpuid));
EXPECT_EQ(arch::Microarchitecture::kAmdFamily0x17, arch::GetMicroarchitecture(cpuid));
auto info = cpuid.Read<arch::CpuidVersionInfo>();
EXPECT_EQ(0x17u, info.family());
EXPECT_EQ(0x08u, info.model());
EXPECT_EQ(0x02u, info.stepping());
EXPECT_EQ("AMD Ryzen Threadripper 2970WX 24-Core Processor"sv, arch::ProcessorName(cpuid).name());
EXPECT_TRUE(arch::HypervisorName(cpuid).name().empty());
{
auto features = cpuid.Read<arch::CpuidFeatureFlagsC>();
// Present:
EXPECT_TRUE(features.rdrand());
EXPECT_TRUE(features.avx());
EXPECT_TRUE(features.osxsave());
EXPECT_TRUE(features.xsave());
EXPECT_TRUE(features.cmpxchg16b());
EXPECT_TRUE(features.monitor());
// Not present:
EXPECT_FALSE(features.hypervisor());
EXPECT_FALSE(features.x2apic());
EXPECT_FALSE(features.pdcm());
}
{
auto features = cpuid.Read<arch::CpuidExtendedFeatureFlagsB>();
// Present:
EXPECT_TRUE(features.smap());
EXPECT_TRUE(features.rdseed());
EXPECT_TRUE(features.fsgsbase());
// Not present:
EXPECT_FALSE(features.intel_pt());
}
// 4 die -> 6 cores -> 2 threads each.
ASSERT_NO_FATAL_FAILURE(CheckApicIdDecoding(cpuid, 1, 2, 2));
ASSERT_NO_FATAL_FAILURE(CheckCaches( //
cpuid, //
{
{
.level = 1,
.type = arch::X86CacheType::kData,
.size_kb = 32,
.ways_of_associativity = 8,
},
{
.level = 1,
.type = arch::X86CacheType::kInstruction,
.size_kb = 64,
.ways_of_associativity = 4,
},
{
.level = 2,
.type = arch::X86CacheType::kUnified,
.size_kb = 512,
.ways_of_associativity = 8,
},
{
.level = 3,
.type = arch::X86CacheType::kUnified,
.size_kb = 8192,
.ways_of_associativity = 16,
},
}));
}
} // namespace