#include <string.h>
#include <util.h>
#include <arch/machine.h>
#define X86_CPUID_VENDOR_STRING_INTEL "GenuineIntel"
#define X86_CPUID_VENDOR_STRING_AMD_LEGACY "AMDisbetter!"
#define X86_CPUID_VENDOR_STRING_AMD "AuthenticAMD"
BOOT_BSS static cpu_identity_t cpu_identity;
BOOT_CODE cpu_identity_t *x86_cpuid_get_identity(void)
{
return &cpu_identity;
}
BOOT_CODE x86_cpu_identity_t *x86_cpuid_get_model_info(void)
{
return &cpu_identity.display;
}
BOOT_CODE static void
x86_cpuid_fill_vendor_string(cpu_identity_t *ci)
{
MAY_ALIAS uint32_t *vendor_string32 = (uint32_t *)ci->vendor_string;
if (ci == NULL) {
return;
}
vendor_string32[0] = x86_cpuid_ebx(0, 0);
vendor_string32[1] = x86_cpuid_edx(0, 0);
vendor_string32[2] = x86_cpuid_ecx(0, 0);
ci->vendor_string[X86_CPUID_VENDOR_STRING_MAXLENGTH] = '\0';
}
struct family_model {
uint8_t family, model;
};
BOOT_CODE static void
x86_cpuid_intel_identity_initialize(cpu_identity_t *ci,
struct family_model original)
{
if (original.family != 0x0F) {
ci->display.family = original.family;
} else {
ci->display.family = ci->display.extended_family + original.family;
}
if (ci->display.family == 0x06 || ci->display.family == 0x0F) {
ci->display.model = (ci->display.extended_model << 4u) + original.model;
} else {
ci->display.model = original.model;
}
}
BOOT_CODE static void
x86_cpuid_amd_identity_initialize(cpu_identity_t *ci,
struct family_model original)
{
if (original.family < 0xF) {
ci->display.family = original.family;
ci->display.model = original.model;
} else {
ci->display.family = original.family + ci->display.extended_family;
ci->display.family = (ci->display.extended_model << 4u) + original.model;
}
}
bool_t
x86_cpuid_initialize(void)
{
cpu_identity_t *ci = x86_cpuid_get_identity();
struct family_model original;
cpuid_001h_eax_t eax;
cpuid_001h_ebx_t ebx;
memset(ci, 0, sizeof(*ci));
x86_cpuid_fill_vendor_string(ci);
eax.words[0] = x86_cpuid_eax(1, 0);
ebx.words[0] = x86_cpuid_ebx(1, 0);
original.family = cpuid_001h_eax_get_family(eax);
original.model = cpuid_001h_eax_get_model(eax);
ci->display.stepping = cpuid_001h_eax_get_stepping(eax);
ci->display.extended_family = cpuid_001h_eax_get_extended_family(eax);
ci->display.extended_model = cpuid_001h_eax_get_extended_model(eax);
ci->display.brand = cpuid_001h_ebx_get_brand(ebx);
if (strncmp(ci->vendor_string, X86_CPUID_VENDOR_STRING_INTEL,
X86_CPUID_VENDOR_STRING_MAXLENGTH) == 0) {
ci->vendor = X86_VENDOR_INTEL;
x86_cpuid_intel_identity_initialize(ci, original);
return true;
} else if (strncmp(ci->vendor_string, X86_CPUID_VENDOR_STRING_AMD_LEGACY,
X86_CPUID_VENDOR_STRING_MAXLENGTH) == 0
|| strncmp(ci->vendor_string, X86_CPUID_VENDOR_STRING_AMD,
X86_CPUID_VENDOR_STRING_MAXLENGTH) == 0) {
ci->vendor = X86_VENDOR_AMD;
x86_cpuid_amd_identity_initialize(ci, original);
return true;
} else {
ci->vendor = X86_VENDOR_OTHER;
return false;
}
}