Doesn't really change the functionality of the code except it'll try to work with less fpu features present.
237 lines
8.0 KiB
C
237 lines
8.0 KiB
C
/*
|
|
* Copyright (c) 2019 Travis Geiselbrecht
|
|
* Copyright 2016 The Fuchsia Authors
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction,
|
|
* including without limitation the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
#include <arch/x86/feature.h>
|
|
|
|
#include <lk/bits.h>
|
|
#include <lk/debug.h>
|
|
#include <lk/trace.h>
|
|
#include <arch/x86.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#define LOCAL_TRACE 0
|
|
|
|
enum x86_cpu_vendor __x86_cpu_vendor = X86_CPU_VENDOR_INTEL;
|
|
enum x86_cpu_level __x86_cpu_level = X86_CPU_LEVEL_386; // start off assuming 386
|
|
|
|
bool has_cpuid = false;
|
|
|
|
/* a saved cache of three banks of cpuids loaded a boot */
|
|
struct x86_cpuid_leaf saved_cpuids[__X86_MAX_SUPPORTED_CPUID + 1];
|
|
struct x86_cpuid_leaf saved_cpuids_hyp[__X86_MAX_SUPPORTED_CPUID_HYP - X86_CPUID_HYP_BASE + 1];
|
|
struct x86_cpuid_leaf saved_cpuids_ext[__X86_MAX_SUPPORTED_CPUID_EXT - X86_CPUID_EXT_BASE + 1];
|
|
uint32_t max_cpuid_leaf = 0;
|
|
uint32_t max_cpuid_leaf_hyp = 0;
|
|
uint32_t max_cpuid_leaf_ext = 0;
|
|
|
|
static enum x86_cpu_vendor match_cpu_vendor_string(const char *str) {
|
|
// from table at https://www.sandpile.org/x86/cpuid.htm#level_0000_0000h
|
|
if (!strcmp(str, "GenuineIntel")) {
|
|
return X86_CPU_VENDOR_INTEL;
|
|
}
|
|
if (!strcmp(str, "UMC UMC UMC ")) {
|
|
return X86_CPU_VENDOR_UMC;
|
|
}
|
|
if (!strcmp(str, "AuthenticAMD")) {
|
|
return X86_CPU_VENDOR_AMD;
|
|
}
|
|
if (!strcmp(str, "CyrixInstead")) {
|
|
return X86_CPU_VENDOR_CYRIX;
|
|
}
|
|
if (!strcmp(str, "NexGenDriven")) {
|
|
return X86_CPU_VENDOR_NEXGEN;
|
|
}
|
|
if (!strcmp(str, "CentaurHauls")) {
|
|
return X86_CPU_VENDOR_CENTAUR;
|
|
}
|
|
if (!strcmp(str, "RiseRiseRise")) {
|
|
return X86_CPU_VENDOR_RISE;
|
|
}
|
|
if (!strcmp(str, "SiS SiS SiS ")) {
|
|
return X86_CPU_VENDOR_SIS;
|
|
}
|
|
if (!strcmp(str, "GenuineTMx86")) {
|
|
return X86_CPU_VENDOR_TRANSMETA;
|
|
}
|
|
if (!strcmp(str, "Geode by NSC")) {
|
|
return X86_CPU_VENDOR_NSC;
|
|
}
|
|
return X86_CPU_VENDOR_UNKNOWN;
|
|
}
|
|
|
|
static void x86_cpu_detect(void) {
|
|
if (X86_LEGACY) {
|
|
// inspired by http://www.rcollins.org/ddj/Sep96/Sep96.html
|
|
// try to detect a 486
|
|
// set the EFLAGS.AC bit, see if it sets
|
|
uint32_t flags = x86_save_flags();
|
|
x86_restore_flags(flags | X86_FLAGS_AC);
|
|
if (x86_save_flags() & X86_FLAGS_AC) {
|
|
__x86_cpu_level = X86_CPU_LEVEL_486;
|
|
|
|
// test EFLAGS.ID flag
|
|
x86_restore_flags(flags | X86_FLAGS_ID);
|
|
if (x86_save_flags() & X86_FLAGS_ID) {
|
|
has_cpuid = true;
|
|
}
|
|
}
|
|
} else {
|
|
// at least a pentium and has cpuid
|
|
__x86_cpu_level = X86_CPU_LEVEL_PENTIUM;
|
|
has_cpuid = true;
|
|
}
|
|
|
|
if (has_cpuid) {
|
|
uint32_t a, b, c, d;
|
|
|
|
// read the max basic cpuid leaf
|
|
cpuid(X86_CPUID_BASE, &a, &b, &c, &d);
|
|
max_cpuid_leaf = a;
|
|
|
|
// read the vendor string
|
|
union {
|
|
uint32_t reg[3];
|
|
char str[13];
|
|
} vs;
|
|
vs.reg[0] = b;
|
|
vs.reg[1] = d;
|
|
vs.reg[2] = c;
|
|
vs.str[12] = 0;
|
|
__x86_cpu_vendor = match_cpu_vendor_string(vs.str);
|
|
|
|
dprintf(SPEW, "x86: vendor string '%s' from cpuid\n", vs.str);
|
|
|
|
// read max extended cpuid leaf
|
|
cpuid(X86_CPUID_EXT_BASE, &a, &b, &c, &d);
|
|
if (a >= X86_CPUID_EXT_BASE) {
|
|
max_cpuid_leaf_ext = a;
|
|
}
|
|
|
|
// read max hypervisor leaf
|
|
cpuid(X86_CPUID_HYP_BASE, &a, &b, &c, &d);
|
|
if (a >= X86_CPUID_HYP_BASE) {
|
|
max_cpuid_leaf_hyp = a;
|
|
}
|
|
} else {
|
|
__x86_cpu_vendor = X86_CPU_VENDOR_INTEL; // intrinsically Intel without cpuid
|
|
}
|
|
|
|
// do a quick cpu level detection using cpuid
|
|
if (has_cpuid && max_cpuid_leaf >= 1) {
|
|
uint32_t a, b, c, d;
|
|
cpuid(X86_CPUID_MODEL_FEATURES, &a, &b, &c, &d);
|
|
|
|
LTRACEF("cpuid leaf 1: %#x %#x %#x %#x\n", a, b, c, d);
|
|
|
|
uint32_t ext_family = BITS_SHIFT(a, 27, 20);
|
|
uint32_t ext_model = BITS_SHIFT(a, 19, 16);
|
|
uint32_t family = BITS_SHIFT(a, 11, 8);
|
|
uint32_t model = BITS_SHIFT(a, 7, 4);
|
|
LTRACEF("raw family %#x model %#x ext_family %#x ext_model %#x\n", family, model, ext_family, ext_model);
|
|
|
|
switch (family) {
|
|
case 4:
|
|
__x86_cpu_level = X86_CPU_LEVEL_486;
|
|
break;
|
|
case 5:
|
|
__x86_cpu_level = X86_CPU_LEVEL_PENTIUM;
|
|
break;
|
|
case 6:
|
|
__x86_cpu_level = X86_CPU_LEVEL_PENTIUM_PRO;
|
|
if (x86_get_cpu_vendor() == X86_CPU_VENDOR_INTEL) {
|
|
model |= ext_model << 4; // extended model field extends the regular model
|
|
}
|
|
break;
|
|
case 0xf:
|
|
__x86_cpu_level = X86_CPU_LEVEL_PENTIUM_PRO;
|
|
family += ext_family; // family 0xf stuff is extended by bits 27:20
|
|
model |= ext_model << 4; // extended model field extends the regular model
|
|
break;
|
|
default:
|
|
// unhandled decode, assume ppro+ level
|
|
__x86_cpu_level = X86_CPU_LEVEL_PENTIUM_PRO;
|
|
break;
|
|
}
|
|
dprintf(SPEW, "x86: family %#x model %#x\n", family, model);
|
|
|
|
// TODO: save this information for future use
|
|
}
|
|
|
|
dprintf(SPEW, "x86: detected cpu level %d has_cpuid %d\n", x86_get_cpu_level(), has_cpuid);
|
|
if (has_cpuid) {
|
|
dprintf(SPEW, "x86: max cpuid leaf %#x ext %#x hyp %#x\n",
|
|
max_cpuid_leaf, max_cpuid_leaf_ext, max_cpuid_leaf_hyp);
|
|
}
|
|
}
|
|
|
|
void x86_feature_early_init(void) {
|
|
x86_cpu_detect();
|
|
|
|
// cache a copy of the cpuid bits
|
|
if (has_cpuid) {
|
|
for (uint32_t i = 1; i <= max_cpuid_leaf; i++) {
|
|
cpuid_c(i, 0, &saved_cpuids[i].a, &saved_cpuids[i].b, &saved_cpuids[i].c, &saved_cpuids[i].d);
|
|
}
|
|
|
|
if (max_cpuid_leaf_ext > 0) {
|
|
for (uint32_t i = X86_CPUID_EXT_BASE + 1; i - 1 < max_cpuid_leaf_ext; i++) {
|
|
uint32_t index = i - X86_CPUID_EXT_BASE;
|
|
cpuid_c(i, 0, &saved_cpuids_ext[index].a, &saved_cpuids_ext[index].b, &saved_cpuids_ext[index].c,
|
|
&saved_cpuids_ext[index].d);
|
|
}
|
|
}
|
|
|
|
if (max_cpuid_leaf_hyp > 0) {
|
|
for (uint32_t i = X86_CPUID_HYP_BASE + 1; i - 1 < max_cpuid_leaf_hyp; i++) {
|
|
uint32_t index = i - X86_CPUID_HYP_BASE;
|
|
cpuid_c(i, 0, &saved_cpuids_hyp[index].a, &saved_cpuids_hyp[index].b, &saved_cpuids_hyp[index].c,
|
|
&saved_cpuids_hyp[index].d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void x86_feature_init(void) {
|
|
}
|
|
|
|
bool x86_get_cpuid_subleaf(enum x86_cpuid_leaf_num num, uint32_t subleaf, struct x86_cpuid_leaf* leaf) {
|
|
// make sure the leaf number is within the detected range of the three blocks we know about
|
|
if (num < X86_CPUID_HYP_BASE) {
|
|
if (num > max_cpuid_leaf) {
|
|
return false;
|
|
}
|
|
} else if (num < X86_CPUID_EXT_BASE) {
|
|
if (num > max_cpuid_leaf_hyp) {
|
|
return false;
|
|
}
|
|
} else if (num > max_cpuid_leaf_ext) {
|
|
return false;
|
|
}
|
|
|
|
cpuid_c((uint32_t)num, subleaf, &leaf->a, &leaf->b, &leaf->c, &leaf->d);
|
|
return true;
|
|
}
|
|
|
|
|