/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2018 Danny Robson */ #include "cpuid_x86.hpp" #include "endian.hpp" #include "view.hpp" #include "format.hpp" #include "bitwise.hpp" #include #include using util::cpu::x86; /////////////////////////////////////////////////////////////////////////////// auto cpuid (int query, int param = 0) { struct { uint32_t a, b, c, d; } res; asm ( "cpuid" : "=a" (res.a), "=b" (res.b), "=c" (res.c), "=d" (res.d) : "a" (query), "c" (param) ); return res; } /////////////////////////////////////////////////////////////////////////////// x86::x86 () { const auto &[largest_function, vendor0, vendor2, vendor1] = cpuid (0); (void)largest_function; 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 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); cores.logical = from_bits (features.b, 23, 16); } { const auto product0 = cpuid (0x8000'0002); const auto product1 = cpuid (0x8000'0003); const auto product2 = cpuid (0x8000'0004); memcpy (&product_name[0x00], &product0, sizeof (product0)); memcpy (&product_name[0x10], &product1, sizeof (product1)); 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 (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; } } /////////////////////////////////////////////////////////////////////////////// std::ostream& util::cpu::operator<< (std::ostream &os, const x86 &val) { return os << util::format::printf ( "{ name: { vendor: '%!', product: '%!' }" ", cores: { logical: %!, physical: %!, hyper_threading: %! }" ", simd { sse: %!, sse2: %!, sse3: %!, ssse3: %!, sse41: %!, sse42: %! }" " }", util::view {val.vendor_name}, util::view {val.product_name}, val.cores.logical, val.cores.physical, val.cores.hyper_threading, val.simd.sse, val.simd.sse2, val.simd.sse3, val.simd.ssse3, val.simd.sse41, val.simd.sse42 ); }