libcruft-util/cpuid/x86.cpp

186 lines
5.0 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
*/
#include "x86.hpp"
#include "endian.hpp"
#include "view.hpp"
#include "bitwise.hpp"
#include <fmt/ostream.h>
#include <cstdint>
#include <ostream>
using cruft::cpu::x86;
///////////////////////////////////////////////////////////////////////////////
namespace {
struct cpuid_t {
uint32_t a, b, c, d;
};
}
//-----------------------------------------------------------------------------
cpuid_t cpuid (u32 query, u32 param = 0)
{
cpuid_t res;
asm (
"cpuid"
: "=a" (res.a), "=b" (res.b), "=c" (res.c), "=d" (res.d)
: "a" (query), "c" (param)
);
return res;
}
///////////////////////////////////////////////////////////////////////////////
namespace {
enum vendor_t {
INTEL,
AMD,
UNKNOWN
};
}
//-----------------------------------------------------------------------------
static constexpr
vendor_t
find_vendor (cpuid_t const &leaf0)
{
constexpr struct {
u32 b, c, d;
vendor_t vendor;
} VALUES[] = {
{ 0x68747541, 0x444d4163, 0x69746e65, AMD },
{ 0x756e6547, 0x6c65746e, 0x49656e69, INTEL },
};
for (auto const &v: VALUES) {
if (v.b == leaf0.b && v.c == leaf0.c && v.d == leaf0.d)
return v.vendor;
}
return UNKNOWN;
}
///////////////////////////////////////////////////////////////////////////////
x86::x86 ()
{
auto const leaf0 = cpuid (0);
memcpy (vendor_name.data () + 0, &leaf0.b, sizeof (leaf0.b));
memcpy (vendor_name.data () + 4, &leaf0.d, sizeof (leaf0.d));
memcpy (vendor_name.data () + 8, &leaf0.c, sizeof (leaf0.c));
auto const vendor = find_vendor (leaf0);
{
auto features = cpuid (1);
simd.sse3 = features.c & (1u << 0);
simd.ssse3 = features.c & (1u << 9);
simd.sse41 = features.c & (1u << 19);
simd.sse42 = features.c & (1u << 20);
simd.avx = features.c & (1u << 28);
simd.sse = features.d & (1u << 25);
simd.sse2 = features.d & (1u << 26);
cores.hyper_threading = features.d & (1u << 28);
if (cores.hyper_threading)
cores.logical = from_bits (features.b, 23, 16);
else
cores.logical = 0;
}
{
const auto product0 = cpuid (0x8000'0002u);
const auto product1 = cpuid (0x8000'0003u);
const auto product2 = cpuid (0x8000'0004u);
memcpy (&product_name[0x00], &product0, sizeof (product0));
memcpy (&product_name[0x10], &product1, sizeof (product1));
memcpy (&product_name[0x20], &product2, sizeof (product2));
}
if (vendor == INTEL) {
cores.logical = cores.physical = 0;
for (int level = 0; true; ++level) {
auto const param = cpuid (0x4, level);
// The final level will set the 'cache type' bits to zero to
// indicate there are no more caches.
if ((param.a & 0b1111) == 0)
break;
cores.physical = max (
cores.physical,
cruft::cast::lossless<int> (from_bits (param.a, 31, 26)) + 1
);
cores.logical = max (
cores.physical,
cruft::cast::lossless<int> (from_bits (param.a, 25, 14)) + 1
);
}
} else if (vendor == AMD) {
auto const size_identifiers = cpuid (0x8000'0008);
if (auto const apic_id_size = from_bits (size_identifiers.c, 15, 12); apic_id_size) {
cores.physical = 1 << (apic_id_size - 1);
} else {
cores.physical = cruft::cast::lossless<int> (size_identifiers.c & 0xff) + 1;
}
} else {
cores.physical = 0;
}
}
///////////////////////////////////////////////////////////////////////////////
std::ostream&
cruft::cpu::operator<< (std::ostream &os, const x86 &val)
{
auto const to_string = [] (auto const &obj) {
return std::string_view {
obj.begin (),
std::find (obj.begin (), obj.end (), '\0')
};
};
fmt::print (
os,
FMT_STRING(
"{{ name: {{ vendor: '{}', product: '{}' }}"
", cores: {{ logical: {}, physical: {}, hyper_threading: {} }}"
", simd: {{ sse: {}, sse2: {}, sse3: {}, ssse3: {}, sse41: {}, sse42: {}, avx: {} }}"
" }}"
),
to_string (val.vendor_name),
to_string (val.product_name),
val.cores.logical,
val.cores.physical,
(val.cores.hyper_threading ? "true" : "false"),
(val.simd.sse ? "true" : "false"),
(val.simd.sse2 ? "true" : "false"),
(val.simd.sse3 ? "true" : "false"),
(val.simd.ssse3 ? "true" : "false"),
(val.simd.sse41 ? "true" : "false"),
(val.simd.sse42 ? "true" : "false"),
(val.simd.avx ? "true" : "false")
);
return os;
}