Add CPU feature checking.

Introduces the cpu_features.inc table to enumerate the CPU features that
j6 cares about. Features in this table marked CPU_FEATURE_REQ are
considered required, and the boot process will log an error and halt
when any of these features are not supported. This should save me from
banging my head against the wall like I did last night with the missing
pdpe1gb feature.
This commit is contained in:
Justin C. Miller
2019-07-04 16:41:25 -07:00
parent 7ce418aabc
commit 3a68ec439d
4 changed files with 138 additions and 45 deletions

View File

@@ -5,6 +5,9 @@
cpu_data bsp_cpu_data;
static constexpr uint32_t cpuid_extended = 0x80000000;
inline static void
__cpuid(
uint32_t leaf,
@@ -25,42 +28,89 @@ __cpuid(
if (edx) *edx = d;
}
cpu_id::cpu_id()
cpu_id::cpu_id() :
m_features(0)
{
__cpuid(0, 0,
&m_high_leaf,
&m_high_basic,
reinterpret_cast<uint32_t *>(&m_vendor_id[0]),
reinterpret_cast<uint32_t *>(&m_vendor_id[8]),
reinterpret_cast<uint32_t *>(&m_vendor_id[4]));
uint32_t eax = 0;
__cpuid(0, 1, &eax);
m_stepping = eax & 0xf;
m_model = (eax >> 4) & 0xf;
m_family = (eax >> 8) & 0xf;
m_cpu_type = (eax >> 12) & 0x3;
uint32_t ext_model = (eax >> 16) & 0xf;
uint32_t ext_family = (eax >> 20) & 0xff;
if (m_family == 0x6 || m_family == 0xf)
m_model = (ext_model << 4) + m_model;
if (m_family == 0xf)
m_family += ext_family;
__cpuid(cpuid_extended, 0, &m_high_ext);
if (m_high_ext >= cpuid_extended + 4) {
__cpuid(cpuid_extended + 2, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[0]),
reinterpret_cast<uint32_t *>(&m_brand_name[4]),
reinterpret_cast<uint32_t *>(&m_brand_name[8]),
reinterpret_cast<uint32_t *>(&m_brand_name[12]));
__cpuid(cpuid_extended + 3, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[16]),
reinterpret_cast<uint32_t *>(&m_brand_name[20]),
reinterpret_cast<uint32_t *>(&m_brand_name[24]),
reinterpret_cast<uint32_t *>(&m_brand_name[28]));
__cpuid(cpuid_extended + 4, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[32]),
reinterpret_cast<uint32_t *>(&m_brand_name[36]),
reinterpret_cast<uint32_t *>(&m_brand_name[40]),
reinterpret_cast<uint32_t *>(&m_brand_name[44]));
} else {
m_brand_name[0] = 0;
}
}
cpu_id::regs
cpu_id::get(uint32_t leaf, uint32_t sub) const
{
if (leaf > m_high_leaf) return {};
regs ret {0, 0, 0, 0};
if ((leaf & cpuid_extended) == 0 && leaf > m_high_basic) return ret;
if ((leaf & cpuid_extended) != 0 && leaf > m_high_ext) return ret;
regs ret;
__cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx);
return ret;
}
void
cpu_id::validate()
{
bool fail = false;
uint32_t leaf = 0;
uint32_t sub = 0;
regs r;
log::info(logs::boot, "CPU: %s", brand_name());
log::debug(logs::boot, " Vendor is %s", vendor_id());
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", highest_basic());
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", highest_ext() & ~cpuid_extended);
#define CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit) \
if (leaf != feat_leaf || sub != feat_sub) { \
leaf = feat_leaf; sub = feat_sub; r = get(leaf, sub); \
} \
if (r.regname & (1 << bit)) \
m_features |= (1 << static_cast<uint64_t>(cpu_feature::name)); \
log::debug(logs::boot, " Supports %9s: %s", #name, (r.regname & (1<<bit)) ? "yes" : "no");
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
if ((r.regname & (1 << bit)) == 0) { \
log::error(logs::boot, "CPU missing required feature " #name); \
fail = true; \
}
#include "cpu_features.inc"
#undef CPU_FEATURE_OPT
#undef CPU_FEATURE_REQ
if (fail)
log::fatal(logs::boot, "CPU not supported.");
}
bool
cpu_id::has_feature(cpu_feature feat)
{
return (m_features & (1 << static_cast<uint64_t>(feat))) != 0;
}