cpuid/x86: use leaf 80000008 for AMD physical core counts

This commit is contained in:
Danny Robson 2019-06-28 16:57:47 +10:00
parent 471c81c43a
commit 0df4fd4bfa

View File

@ -42,17 +42,48 @@ cpuid_t cpuid (u32 query, u32 param = 0)
}
///////////////////////////////////////////////////////////////////////////////
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 ()
{
const auto &[largest_function, vendor0, vendor2, vendor1] = cpuid (0);
(void)largest_function;
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));
memcpy (vendor_name.data () + 0, &vendor0, sizeof (vendor0));
memcpy (vendor_name.data () + 4, &vendor1, sizeof (vendor1));
memcpy (vendor_name.data () + 8, &vendor2, sizeof (vendor2));
const bool is_amd = vendor0 == 0x68747541 && vendor1 == 0x69746e65 && vendor2 == 0x444d4163;
auto const vendor = find_vendor (leaf0);
{
auto features = cpuid (1);
@ -66,7 +97,10 @@ x86::x86 ()
simd.sse2 = features.d & (1u << 26);
cores.hyper_threading = features.d & (1u << 28);
cores.logical = from_bits (features.b, 23, 16);
if (cores.hyper_threading)
cores.logical = from_bits (features.b, 23, 16);
else
cores.logical = 0;
}
{
@ -79,16 +113,36 @@ x86::x86 ()
memcpy (&product_name[0x20], &product2, sizeof (product2));
}
// Function 4 isn't implemented by AMD. Intel uses it for cache
// descriptors. It has some useful information (like procesor counts) but
// it's ugly as sin; try to avoid using it.
// { (void)cpuid (0x0b, 4); }
if (vendor == INTEL) {
cores.logical = cores.physical = 0;
if (is_amd) {
auto long_mode = cpuid (0x8000'0008);
auto apic_id_size = from_bits (long_mode.c, 15, 12);
(void)apic_id_size;
cores.physical = 0; //from_bits (long_mode.c, 7, 0) + 1;
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 = (size_identifiers.c & 0xff) + 1;
}
} else {
cores.physical = 0;
}
}