186 lines
5.0 KiB
C++
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 cruft::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;
|
|
}
|