| // Copyright 2016 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| // This file is based on zircon/kernel/arch/x86/feature.c. |
| // TODO(dje): As with generic elf, dwarf, et.al, move to application |
| // independent library. |
| |
| #include "x86_cpuid.h" |
| |
| #include <cpuid.h> |
| #include <atomic> |
| #include <cstdint> |
| #include <cstring> |
| #include <mutex> |
| |
| namespace debugger_utils { |
| |
| // Trick to get a 1 of the right size. |
| #define ONE(x) (1 + ((x) - (x))) |
| #define BITS_SHIFT(x, high, low) \ |
| (((x) >> (low)) & ((ONE(x) << ((high) - (low) + 1)) - 1)) |
| |
| // Note: cpuid state is constant once computed, and thus isn't guarded. |
| |
| static struct x86_cpuid_leaf cpuid[MAX_SUPPORTED_CPUID + 1]; |
| static struct x86_cpuid_leaf |
| cpuid_ext[MAX_SUPPORTED_CPUID_EXT - X86_CPUID_EXT_BASE + 1]; |
| static uint32_t max_cpuid = 0; |
| static uint32_t max_ext_cpuid = 0; |
| |
| static enum x86_vendor_list x86_vendor; |
| |
| static struct x86_model_info model_info; |
| |
| static std::mutex cpuid_mutex; |
| |
| static bool initialized = false; // TODO(dje): add guard annotation |
| |
| static const x86_cpuid_leaf* x86_get_cpuid_leaf_raw( |
| enum x86_cpuid_leaf_num leaf); |
| |
| void x86_feature_init(void) { |
| std::lock_guard<std::mutex> lock(cpuid_mutex); |
| |
| if (initialized) |
| return; |
| |
| // test for cpuid count |
| __cpuid(0, cpuid[0].a, cpuid[0].b, cpuid[0].c, cpuid[0].d); |
| |
| max_cpuid = cpuid[0].a; |
| if (max_cpuid > MAX_SUPPORTED_CPUID) |
| max_cpuid = MAX_SUPPORTED_CPUID; |
| |
| // figure out the vendor |
| union { |
| uint32_t vendor_id[3]; |
| char vendor_string[13]; |
| } vu; |
| vu.vendor_id[0] = cpuid[0].b; |
| vu.vendor_id[1] = cpuid[0].d; |
| vu.vendor_id[2] = cpuid[0].c; |
| vu.vendor_string[12] = '\0'; |
| if (!strcmp(vu.vendor_string, "GenuineIntel")) { |
| x86_vendor = X86_VENDOR_INTEL; |
| } else if (!strcmp(vu.vendor_string, "AuthenticAMD")) { |
| x86_vendor = X86_VENDOR_AMD; |
| } else { |
| x86_vendor = X86_VENDOR_UNKNOWN; |
| } |
| |
| // read in the base cpuids |
| for (uint32_t i = 1; i <= max_cpuid; i++) { |
| __cpuid_count(i, 0, cpuid[i].a, cpuid[i].b, cpuid[i].c, cpuid[i].d); |
| } |
| |
| // test for extended cpuid count |
| __cpuid(X86_CPUID_EXT_BASE, cpuid_ext[0].a, cpuid_ext[0].b, cpuid_ext[0].c, |
| cpuid_ext[0].d); |
| |
| max_ext_cpuid = cpuid_ext[0].a; |
| if (max_ext_cpuid > MAX_SUPPORTED_CPUID_EXT) |
| max_ext_cpuid = MAX_SUPPORTED_CPUID_EXT; |
| |
| // read in the extended cpuids |
| for (uint32_t i = X86_CPUID_EXT_BASE + 1; i - 1 < max_ext_cpuid; i++) { |
| uint32_t index = i - X86_CPUID_EXT_BASE; |
| __cpuid_count(i, 0, cpuid_ext[index].a, cpuid_ext[index].b, |
| cpuid_ext[index].c, cpuid_ext[index].d); |
| } |
| |
| // populate the model info |
| const struct x86_cpuid_leaf* leaf = |
| x86_get_cpuid_leaf_raw(X86_CPUID_MODEL_FEATURES); |
| if (leaf) { |
| model_info.processor_type = BITS_SHIFT(leaf->a, 13, 12); |
| model_info.family = BITS_SHIFT(leaf->a, 11, 8); |
| model_info.model = BITS_SHIFT(leaf->a, 7, 4); |
| model_info.stepping = BITS_SHIFT(leaf->a, 3, 0); |
| model_info.display_family = model_info.family; |
| model_info.display_model = model_info.model; |
| |
| if (model_info.family == 0xf) { |
| model_info.display_family += BITS_SHIFT(leaf->a, 27, 20); |
| } |
| if (model_info.family == 0xf || model_info.family == 0x6) { |
| model_info.display_model += BITS_SHIFT(leaf->a, 19, 16) << 4; |
| } |
| } |
| |
| initialized = true; |
| } |
| |
| bool x86_get_cpuid_subleaf(enum x86_cpuid_leaf_num num, uint32_t subleaf, |
| struct x86_cpuid_leaf* leaf) { |
| x86_feature_init(); |
| |
| if (num < X86_CPUID_EXT_BASE) { |
| if (num > max_cpuid) |
| return false; |
| } else if (num > max_ext_cpuid) { |
| return false; |
| } |
| |
| __cpuid_count((uint32_t)num, subleaf, leaf->a, leaf->b, leaf->c, leaf->d); |
| return true; |
| } |
| |
| static const x86_cpuid_leaf* x86_get_cpuid_leaf_raw( |
| enum x86_cpuid_leaf_num leaf) { |
| if (leaf < X86_CPUID_EXT_BASE) { |
| if (leaf > max_cpuid) |
| return nullptr; |
| |
| return &cpuid[leaf]; |
| } else { |
| if (leaf > max_ext_cpuid) |
| return nullptr; |
| |
| return &cpuid_ext[(uint32_t)leaf - (uint32_t)X86_CPUID_EXT_BASE]; |
| } |
| } |
| |
| const x86_cpuid_leaf* x86_get_cpuid_leaf(enum x86_cpuid_leaf_num leaf) { |
| x86_feature_init(); |
| return x86_get_cpuid_leaf_raw(leaf); |
| } |
| |
| bool x86_feature_test(struct x86_cpuid_bit bit) { |
| FXL_DCHECK(bit.word <= 3 && bit.bit <= 31); |
| |
| if (bit.word > 3 || bit.bit > 31) |
| return false; |
| |
| const x86_cpuid_leaf* leaf = x86_get_cpuid_leaf(bit.leaf_num); |
| if (!leaf) |
| return false; |
| |
| switch (bit.word) { |
| case 0: |
| return !!((1u << bit.bit) & leaf->a); |
| case 1: |
| return !!((1u << bit.bit) & leaf->b); |
| case 2: |
| return !!((1u << bit.bit) & leaf->c); |
| case 3: |
| return !!((1u << bit.bit) & leaf->d); |
| default: |
| return false; |
| } |
| } |
| |
| bool x86_topology_enumerate(uint8_t level, struct x86_topology_level* info) { |
| uint32_t eax, ebx, ecx, edx; |
| __cpuid_count(X86_CPUID_TOPOLOGY, level, eax, ebx, ecx, edx); |
| |
| uint8_t type = (ecx >> 8) & 0xff; |
| if (type == X86_TOPOLOGY_INVALID) |
| return false; |
| |
| info->right_shift = eax & 0x1f; |
| info->type = type; |
| return true; |
| } |
| |
| const struct x86_model_info* x86_get_model(void) { |
| x86_feature_init(); |
| return &model_info; |
| } |
| |
| // N.B. The output is parsed by spt-sideband.cc. |
| |
| void x86_feature_debug(FILE* out) { |
| static const struct { |
| x86_cpuid_bit bit; |
| const char* name; |
| } features[] = { |
| {X86_FEATURE_FPU, "fpu"}, |
| {X86_FEATURE_SSE, "sse"}, |
| {X86_FEATURE_SSE2, "sse2"}, |
| {X86_FEATURE_SSE3, "sse3"}, |
| {X86_FEATURE_SSSE3, "ssse3"}, |
| {X86_FEATURE_SSE4_1, "sse4.1"}, |
| {X86_FEATURE_SSE4_2, "sse4.2"}, |
| {X86_FEATURE_MMX, "mmx"}, |
| {X86_FEATURE_AVX, "avx"}, |
| {X86_FEATURE_AVX2, "avx2"}, |
| {X86_FEATURE_FXSR, "fxsr"}, |
| {X86_FEATURE_XSAVE, "xsave"}, |
| {X86_FEATURE_AESNI, "aesni"}, |
| {X86_FEATURE_TSC_ADJUST, "tsc_adj"}, |
| {X86_FEATURE_SMEP, "smep"}, |
| {X86_FEATURE_SMAP, "smap"}, |
| {X86_FEATURE_RDRAND, "rdrand"}, |
| {X86_FEATURE_RDSEED, "rdseed"}, |
| {X86_FEATURE_PT, "pt"}, |
| {X86_FEATURE_PKU, "pku"}, |
| {X86_FEATURE_SYSCALL, "syscall"}, |
| {X86_FEATURE_NX, "nx"}, |
| {X86_FEATURE_HUGE_PAGE, "huge"}, |
| {X86_FEATURE_RDTSCP, "rdtscp"}, |
| {X86_FEATURE_INVAR_TSC, "invar_tsc"}, |
| {X86_FEATURE_TSC_DEADLINE, "tsc_deadline"}, |
| }; |
| |
| x86_feature_init(); |
| |
| const char* vendor_string; |
| switch (x86_vendor) { |
| default: |
| case X86_VENDOR_UNKNOWN: |
| vendor_string = "unknown"; |
| break; |
| case X86_VENDOR_INTEL: |
| vendor_string = "Intel"; |
| break; |
| case X86_VENDOR_AMD: |
| vendor_string = "AMD"; |
| break; |
| } |
| fprintf(out, "Vendor: %s\n", vendor_string); |
| |
| auto model = x86_get_model(); |
| fprintf(out, "Model:\n"); |
| fprintf(out, " processor: %u\n", model->processor_type); |
| // TODO(dje): It's not clear which "variant" of family/model IPT wants, |
| // but it seems like it would want the more detailed version, so that's |
| // what we use. |
| fprintf(out, " base family: %u\n", model->family); |
| fprintf(out, " base model: %u\n", model->model); |
| fprintf(out, " family: %u\n", model->display_family); |
| fprintf(out, " model: %u\n", model->display_model); |
| fprintf(out, " stepping: %u\n", model->stepping); |
| |
| fprintf(out, "Features:\n"); |
| size_t col = 0; |
| for (auto& f : features) { |
| if (x86_feature_test(f.bit)) |
| col += fprintf(out, " %s", f.name); |
| if (col >= 80) { |
| fprintf(out, "\n"); |
| col = 0; |
| } |
| } |
| if (col > 0) |
| fprintf(out, "\n"); |
| } |
| |
| } // namespace debugger_utils |