blob: 319c8e00534991ec63787106719ddf47e20286f4 [file] [log] [blame]
// 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
#include <arch/x86/feature.h>
#include <assert.h>
#include <bits.h>
#include <stdint.h>
#include <string.h>
#include <trace.h>
#include <arch/ops.h>
#include <fbl/algorithm.h>
#define LOCAL_TRACE 0
struct cpuid_leaf _cpuid[MAX_SUPPORTED_CPUID + 1];
struct cpuid_leaf _cpuid_ext[MAX_SUPPORTED_CPUID_EXT - X86_CPUID_EXT_BASE + 1];
uint32_t max_cpuid = 0;
uint32_t max_ext_cpuid = 0;
enum x86_vendor_list x86_vendor;
enum x86_microarch_list x86_microarch;
static struct x86_model_info model_info;
bool g_x86_feature_smap;
static int initialized = 0;
static enum x86_microarch_list get_microarch(struct x86_model_info* info);
void x86_feature_init(void)
{
if (atomic_swap(&initialized, 1)) {
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;
LTRACEF("max cpuid 0x%x\n", max_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_c(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;
LTRACEF("max extended cpuid 0x%x\n", max_ext_cpuid);
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_c(i, 0, &_cpuid_ext[index].a, &_cpuid_ext[index].b, &_cpuid_ext[index].c, &_cpuid_ext[index].d);
}
/* populate the model info */
const struct cpuid_leaf* leaf = x86_get_cpuid_leaf(X86_CPUID_MODEL_FEATURES);
if (leaf) {
model_info.processor_type = (uint8_t)BITS_SHIFT(leaf->a, 13, 12);
model_info.family = (uint8_t)BITS_SHIFT(leaf->a, 11, 8);
model_info.model = (uint8_t)BITS_SHIFT(leaf->a, 7, 4);
model_info.stepping = (uint8_t)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;
}
x86_microarch = get_microarch(&model_info);
}
g_x86_feature_smap = x86_feature_test(X86_FEATURE_SMAP);
}
static enum x86_microarch_list get_microarch(struct x86_model_info* info) {
if (x86_vendor == X86_VENDOR_INTEL && info->family == 0x6) {
switch (info->display_model) {
case 0x2a: /* Sandy Bridge */
case 0x2d: /* Sandy Bridge EP */
return X86_MICROARCH_INTEL_SANDY_BRIDGE;
case 0x3a: /* Ivy Bridge */
case 0x3e: /* Ivy Bridge EP */
return X86_MICROARCH_INTEL_IVY_BRIDGE;
case 0x3c: /* Haswell DT */
case 0x3f: /* Haswell MB */
case 0x45: /* Haswell ULT */
case 0x46: /* Haswell ULX */
return X86_MICROARCH_INTEL_HASWELL;
case 0x3d: /* Broadwell */
case 0x47: /* Broadwell H */
case 0x56: /* Broadwell EP */
case 0x4f: /* Broadwell EX */
return X86_MICROARCH_INTEL_BROADWELL;
case 0x4e: /* Skylake Y/U */
case 0x5e: /* Skylake H/S */
case 0x55: /* Skylake E */
return X86_MICROARCH_INTEL_SKYLAKE;
case 0x8e: /* Kabylake Y/U */
case 0x9e: /* Kabylake H/S */
return X86_MICROARCH_INTEL_KABYLAKE;
}
} else if (x86_vendor == X86_VENDOR_AMD && info->family == 0xf) {
switch (info->display_family) { // zen
case 0x15: /* Bulldozer */
return X86_MICROARCH_AMD_BULLDOZER;
case 0x16: /* Jaguar */
return X86_MICROARCH_AMD_JAGUAR;
case 0x17: /* Zen */
return X86_MICROARCH_AMD_ZEN;
}
}
return X86_MICROARCH_UNKNOWN;
}
bool x86_get_cpuid_subleaf(
enum x86_cpuid_leaf_num num, uint32_t subleaf, struct cpuid_leaf *leaf)
{
if (num < X86_CPUID_EXT_BASE) {
if (num > max_cpuid)
return false;
} else if (num > max_ext_cpuid) {
return false;
}
cpuid_c((uint32_t)num, subleaf, &leaf->a, &leaf->b, &leaf->c, &leaf->d);
return true;
}
bool x86_topology_enumerate(uint8_t level, struct x86_topology_level *info)
{
DEBUG_ASSERT(info);
uint32_t eax, ebx, ecx, edx;
cpuid_c(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)
{
return &model_info;
}
void x86_feature_debug(void)
{
static struct {
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_FSGSBASE, "fsgsbase" },
{ X86_FEATURE_TSC_ADJUST, "tsc_adj" },
{ X86_FEATURE_SMEP, "smep" },
{ X86_FEATURE_SMAP, "smap" },
{ X86_FEATURE_RDRAND, "rdrand" },
{ X86_FEATURE_RDSEED, "rdseed" },
{ 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_VMX, "vmx" },
{ X86_FEATURE_HYPERVISOR, "hypervisor" },
{ X86_FEATURE_PT, "pt" },
{ X86_FEATURE_HWP, "hwp" },
};
const char *vendor_string = NULL;
switch (x86_vendor) {
case X86_VENDOR_UNKNOWN: vendor_string = "unknown"; break;
case X86_VENDOR_INTEL: vendor_string = "Intel"; break;
case X86_VENDOR_AMD: vendor_string = "AMD"; break;
}
printf("Vendor: %s\n", vendor_string);
const char *microarch_string = NULL;
switch (x86_microarch) {
case X86_MICROARCH_UNKNOWN: microarch_string = "unknown"; break;
case X86_MICROARCH_INTEL_SANDY_BRIDGE: microarch_string = "Sandy Bridge"; break;
case X86_MICROARCH_INTEL_IVY_BRIDGE: microarch_string = "Ivy Bridge"; break;
case X86_MICROARCH_INTEL_BROADWELL: microarch_string = "Broadwell"; break;
case X86_MICROARCH_INTEL_HASWELL: microarch_string = "Haswell"; break;
case X86_MICROARCH_INTEL_SKYLAKE: microarch_string = "Skylake"; break;
case X86_MICROARCH_INTEL_KABYLAKE: microarch_string = "Kaby Lake"; break;
case X86_MICROARCH_AMD_BULLDOZER: microarch_string = "Bulldozer"; break;
case X86_MICROARCH_AMD_JAGUAR: microarch_string = "Jaguar"; break;
case X86_MICROARCH_AMD_ZEN: microarch_string = "Zen"; break;
}
printf("Microarch: %s\n", microarch_string);
printf("Features: ");
uint col = 0;
for (uint i = 0; i < fbl::count_of(features); ++i) {
if (x86_feature_test(features[i].bit))
col += printf("%s ", features[i].name);
if (col >= 80) {
printf("\n");
col = 0;
}
}
if (col > 0)
printf("\n");
}