blob: 43aa8d9a4fa673ed5e150de256561ad22319bc99 [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/cpuid.h>
#include <iomanip>
#include <gtest/gtest.h>
namespace {
TEST(CpuidTests, Family) {
{
auto version =
arch::CpuidVersionInfo::Get().FromValue(0).set_base_family(0xf).set_extended_family(0xf0);
EXPECT_EQ(0xff, version.family());
}
// Extended family ID is ignored for other families.
{
auto version = arch::CpuidVersionInfo::Get()
.FromValue(0)
.set_base_family(0x6)
// Suppose this is garbage or an internal detail.
.set_extended_family(0xf0);
EXPECT_EQ(0x06, version.family());
}
}
TEST(CpuidTests, Model) {
{
auto version = arch::CpuidVersionInfo::Get()
.FromValue(0)
.set_base_family(0x6)
.set_base_model(0xa)
.set_extended_model(0xb);
EXPECT_EQ(0xba, version.model());
}
{
auto version = arch::CpuidVersionInfo::Get()
.FromValue(0)
.set_base_family(0xf)
.set_base_model(0xa)
.set_extended_model(0xb);
EXPECT_EQ(0xba, version.model());
}
// Extended model ID is ignored for other families.
{
auto version = arch::CpuidVersionInfo::Get()
.FromValue(0)
.set_base_family(0x1)
.set_base_model(0xa)
// Suppose this is garbage or an internal detail.
.set_extended_model(0xf);
EXPECT_EQ(0x0a, version.model());
}
}
TEST(CpuidTests, GetMicroarchitectureFromVersion) {
// Particular SoCs judiciously picked at random.
struct {
arch::Vendor vendor;
uint8_t extended_family;
uint8_t base_family;
uint8_t extended_model;
uint8_t base_model;
arch::Microarchitecture expected;
} test_cases[] = {
// An unknown vendor should result in an unknown microarchitecture.
{arch::Vendor::kUnknown, 0xa, 0xb, 0xc, 0xd, arch::Microarchitecture::kUnknown},
// Intel Clarksfield.
{arch::Vendor::kIntel, 0x0, 0x6, 0x1, 0xe, arch::Microarchitecture::kIntelNehalem},
// Intel Coffee Lake S.
{arch::Vendor::kIntel, 0x0, 0x6, 0x9, 0xe, arch::Microarchitecture::kIntelSkylake},
// Intel Skylake SP
{arch::Vendor::kIntel, 0x0, 0x6, 0x5, 0x5, arch::Microarchitecture::kIntelSkylakeServer},
// Intel Tangier.
{arch::Vendor::kIntel, 0x0, 0x6, 0x4, 0xa, arch::Microarchitecture::kIntelSilvermont},
// AMD Kaveri.
{arch::Vendor::kAmd, 0x6, 0xf, 0x3, 0x0, arch::Microarchitecture::kAmdFamily0x15},
// AMD Banded Kestrel.
{arch::Vendor::kAmd, 0x8, 0xf, 0x1, 0x8, arch::Microarchitecture::kAmdFamily0x17},
};
for (const auto& test_case : test_cases) {
auto version = arch::CpuidVersionInfo::Get()
.FromValue(0)
.set_extended_family(test_case.extended_family)
.set_base_family(test_case.base_family)
.set_extended_model(test_case.extended_model)
.set_base_model(test_case.base_model);
auto actual = version.microarchitecture(test_case.vendor);
std::string_view actual_sv = arch::ToString(actual);
std::string_view expected_sv = arch::ToString(test_case.expected);
std::string_view vendor_sv = arch::ToString(test_case.vendor);
EXPECT_EQ(test_case.expected, actual)
<< "expected a microarchictecture of " << std::quoted(expected_sv)
<< " for (vendor, extended family, base family, extended model, base model) = "
<< "(" << vendor_sv << ", " << std::hex << test_case.extended_family << ", " << std::hex
<< test_case.base_family << ", " << std::hex << test_case.extended_model << ", " << std::hex
<< test_case.base_model << "); got " << std::quoted(actual_sv);
}
}
TEST(CpuidTests, CpuidSupports) {
using arch::testing::X86Microprocessor;
{
// Max basic leaf: 0xa;
// Max hypervisor leaf: 0x0;
// Max extended leaf: 0x8000'0008.
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kIntelAtom330);
// Supported basic leaf (0xa).
EXPECT_TRUE(arch::CpuidSupports<arch::CpuidPerformanceMonitoringA>(cpuid));
// Unsupported basic leaf (0x14).
EXPECT_FALSE(arch::CpuidSupports<arch::CpuidProcessorTraceMainB>(cpuid));
// Unsupported hypervisor leaf (0x4000'0000)
EXPECT_FALSE(arch::CpuidSupports<arch::CpuidMaximumHypervisorLeaf>(cpuid));
// Supported extended leaf (0x8000'0008).
EXPECT_TRUE(arch::CpuidSupports<arch::CpuidExtendedAmdFeatureFlagsB>(cpuid));
// Unsupported extended leaf (0x8000'001e).
EXPECT_FALSE(arch::CpuidSupports<arch::CpuidExtendedApicId>(cpuid));
}
{
// Max basic leaf: 0x10;
// Max hypervisor leaf: 0x0;
// Max extended leaf: 0x8000'0020.
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen9_3950x);
// Supported 0x8000'001d, 0x8000'001e (has topology extensions).
using CpuidAmdCacheTopologyA_0 = arch::CpuidAmdCacheTopologyA<0>;
EXPECT_TRUE(arch::CpuidSupports<CpuidAmdCacheTopologyA_0>(cpuid));
EXPECT_TRUE(arch::CpuidSupports<arch::CpuidExtendedApicId>(cpuid));
}
{
// Max basic leaf: 0xd;
// Max hypervisor leaf: 0x4000'0010;
// Max extended leaf: 0x8000'001e.
arch::testing::FakeCpuidIo cpuid(X86Microprocessor::kAmdRyzen9_3950xVmware);
// Supported hypervisor leaf (0x4000'0000).
EXPECT_TRUE(arch::CpuidSupports<arch::CpuidMaximumHypervisorLeaf>(cpuid));
// Unsupported 0x8000'001d, 0x8000'001e (no topology extensions).
using CpuidAmdCacheTopologyA_0 = arch::CpuidAmdCacheTopologyA<0>;
EXPECT_FALSE(arch::CpuidSupports<CpuidAmdCacheTopologyA_0>(cpuid));
EXPECT_FALSE(arch::CpuidSupports<arch::CpuidExtendedApicId>(cpuid));
}
}
} // namespace