/* * 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 */ #include "x86.hpp" #include "endian.hpp" #include "view.hpp" #include "bitwise.hpp" #include #include #include 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 (from_bits (param.a, 31, 26)) + 1 ); cores.logical = max ( cores.physical, cruft::cast::lossless (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 (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; }