120 lines
3.6 KiB
C++
120 lines
3.6 KiB
C++
/*
|
|
* 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 <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#include "cpuid_x86.hpp"
|
|
|
|
#include "endian.hpp"
|
|
#include "view.hpp"
|
|
#include "format.hpp"
|
|
#include "bitwise.hpp"
|
|
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
|
|
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
|
|
);
|
|
}
|