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:
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
cpu_data bsp_cpu_data;
|
cpu_data bsp_cpu_data;
|
||||||
|
|
||||||
|
static constexpr uint32_t cpuid_extended = 0x80000000;
|
||||||
|
|
||||||
|
|
||||||
inline static void
|
inline static void
|
||||||
__cpuid(
|
__cpuid(
|
||||||
uint32_t leaf,
|
uint32_t leaf,
|
||||||
@@ -25,42 +28,89 @@ __cpuid(
|
|||||||
if (edx) *edx = d;
|
if (edx) *edx = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpu_id::cpu_id() :
|
||||||
cpu_id::cpu_id()
|
m_features(0)
|
||||||
{
|
{
|
||||||
__cpuid(0, 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[0]),
|
||||||
reinterpret_cast<uint32_t *>(&m_vendor_id[8]),
|
reinterpret_cast<uint32_t *>(&m_vendor_id[8]),
|
||||||
reinterpret_cast<uint32_t *>(&m_vendor_id[4]));
|
reinterpret_cast<uint32_t *>(&m_vendor_id[4]));
|
||||||
|
|
||||||
uint32_t eax = 0;
|
__cpuid(cpuid_extended, 0, &m_high_ext);
|
||||||
__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;
|
|
||||||
|
|
||||||
|
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::regs
|
||||||
cpu_id::get(uint32_t leaf, uint32_t sub) const
|
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);
|
__cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx);
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,40 +23,69 @@ struct cpu_data
|
|||||||
|
|
||||||
extern cpu_data bsp_cpu_data;
|
extern cpu_data bsp_cpu_data;
|
||||||
|
|
||||||
|
/// Enum of the cpu features jsix cares about
|
||||||
|
enum class cpu_feature {
|
||||||
|
#define CPU_FEATURE_REQ(name, ...) name,
|
||||||
|
#define CPU_FEATURE_OPT(name, ...) name,
|
||||||
|
#include "cpu_features.inc"
|
||||||
|
#undef CPU_FEATURE_OPT
|
||||||
|
#undef CPU_FEATURE_REQ
|
||||||
|
max
|
||||||
|
};
|
||||||
|
|
||||||
class cpu_id
|
class cpu_id
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/// CPUID result register values
|
||||||
struct regs {
|
struct regs {
|
||||||
uint32_t eax, ebx, ecx, edx;
|
union {
|
||||||
|
uint32_t reg[4];
|
||||||
regs() : eax(0), ebx(0), ecx(0), edx(0) {}
|
uint32_t eax, ebx, ecx, edx;
|
||||||
regs(uint32_t a, uint32_t b, uint32_t c, uint32_t d) : eax(a), ebx(b), ecx(c), edx(d) {}
|
};
|
||||||
regs(const regs &r) : eax(r.eax), ebx(r.ebx), ecx(r.ecx), edx(r.edx) {}
|
|
||||||
|
|
||||||
|
/// Return true if bit |bit| of EAX is set
|
||||||
bool eax_bit(unsigned bit) { return (eax >> bit) & 0x1; }
|
bool eax_bit(unsigned bit) { return (eax >> bit) & 0x1; }
|
||||||
|
|
||||||
|
/// Return true if bit |bit| of EBX is set
|
||||||
bool ebx_bit(unsigned bit) { return (ebx >> bit) & 0x1; }
|
bool ebx_bit(unsigned bit) { return (ebx >> bit) & 0x1; }
|
||||||
|
|
||||||
|
/// Return true if bit |bit| of ECX is set
|
||||||
bool ecx_bit(unsigned bit) { return (ecx >> bit) & 0x1; }
|
bool ecx_bit(unsigned bit) { return (ecx >> bit) & 0x1; }
|
||||||
|
|
||||||
|
/// Return true if bit |bit| of EDX is set
|
||||||
bool edx_bit(unsigned bit) { return (edx >> bit) & 0x1; }
|
bool edx_bit(unsigned bit) { return (edx >> bit) & 0x1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
cpu_id();
|
cpu_id();
|
||||||
|
|
||||||
|
/// The the result of a given CPUID leaf/subleaf
|
||||||
|
/// \arg leaf The leaf selector (initial EAX)
|
||||||
|
/// \arg subleaf The subleaf selector (initial ECX)
|
||||||
|
/// \returns A |regs| struct of the values retuned
|
||||||
regs get(uint32_t leaf, uint32_t sub = 0) const;
|
regs get(uint32_t leaf, uint32_t sub = 0) const;
|
||||||
|
|
||||||
|
/// Get the name of the cpu vendor (eg, "GenuineIntel")
|
||||||
inline const char * vendor_id() const { return m_vendor_id; }
|
inline const char * vendor_id() const { return m_vendor_id; }
|
||||||
inline uint8_t cpu_type() const { return m_cpu_type; }
|
|
||||||
inline uint8_t stepping() const { return m_stepping; }
|
/// Get the brand name of this processor model
|
||||||
inline uint16_t family() const { return m_family; }
|
inline const char * brand_name() const { return m_brand_name; }
|
||||||
inline uint16_t model() const { return m_model; }
|
|
||||||
|
/// Get the highest basic CPUID leaf supported
|
||||||
|
inline uint32_t highest_basic() const { return m_high_basic; }
|
||||||
|
|
||||||
|
/// Get the highest extended CPUID leaf supported
|
||||||
|
inline uint32_t highest_ext() const { return m_high_ext; }
|
||||||
|
|
||||||
|
/// Validate the CPU supports the necessary options for jsix
|
||||||
|
void validate();
|
||||||
|
|
||||||
|
/// Return true if the CPU claims to support the given feature
|
||||||
|
bool has_feature(cpu_feature feat);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void read();
|
uint32_t m_high_basic;
|
||||||
|
uint32_t m_high_ext;
|
||||||
uint32_t m_high_leaf;
|
|
||||||
char m_vendor_id[13];
|
char m_vendor_id[13];
|
||||||
|
char m_brand_name[48];
|
||||||
uint8_t m_cpu_type;
|
uint64_t m_features;
|
||||||
uint8_t m_stepping;
|
|
||||||
uint16_t m_family;
|
|
||||||
uint16_t m_model;
|
|
||||||
};
|
};
|
||||||
|
|||||||
16
src/kernel/cpu_features.inc
Normal file
16
src/kernel/cpu_features.inc
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
CPU_FEATURE_OPT(pcid, 0x00000001, 0, ecx, 17)
|
||||||
|
CPU_FEATURE_OPT(x2apic, 0x00000001, 0, ecx, 21)
|
||||||
|
CPU_FEATURE_REQ(fpu, 0x00000001, 0, edx, 0)
|
||||||
|
CPU_FEATURE_REQ(pse, 0x00000001, 0, edx, 3)
|
||||||
|
CPU_FEATURE_OPT(tsc, 0x00000001, 0, edx, 4)
|
||||||
|
CPU_FEATURE_REQ(msr, 0x00000001, 0, edx, 5)
|
||||||
|
CPU_FEATURE_REQ(apic, 0x00000001, 0, edx, 9)
|
||||||
|
CPU_FEATURE_REQ(pge, 0x00000001, 0, edx, 13)
|
||||||
|
CPU_FEATURE_OPT(pat, 0x00000001, 0, edx, 16)
|
||||||
|
CPU_FEATURE_REQ(fxsr, 0x00000001, 0, edx, 24)
|
||||||
|
CPU_FEATURE_OPT(fsgsbase, 0x00000007, 0, ebx, 0)
|
||||||
|
CPU_FEATURE_OPT(invpcid, 0x00000007, 0, ebx, 10)
|
||||||
|
CPU_FEATURE_OPT(pku, 0x00000007, 0, ecx, 3)
|
||||||
|
CPU_FEATURE_REQ(syscall, 0x80000001, 0, edx, 11)
|
||||||
|
CPU_FEATURE_REQ(pdpe1gb, 0x80000001, 0, edx, 26)
|
||||||
|
CPU_FEATURE_OPT(extapic, 0x80000001, 0, ecx, 3)
|
||||||
@@ -78,6 +78,9 @@ kernel_main(kernel_args *header)
|
|||||||
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
|
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
|
||||||
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime);
|
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime);
|
||||||
|
|
||||||
|
cpu_id cpu;
|
||||||
|
cpu.validate();
|
||||||
|
|
||||||
initrd::disk ird(header->initrd, heap);
|
initrd::disk ird(header->initrd, heap);
|
||||||
log::info(logs::boot, "initrd loaded with %d files.", ird.files().count());
|
log::info(logs::boot, "initrd loaded with %d files.", ird.files().count());
|
||||||
for (auto &f : ird.files())
|
for (auto &f : ird.files())
|
||||||
@@ -94,11 +97,6 @@ kernel_main(kernel_args *header)
|
|||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
cpu_id cpu;
|
|
||||||
log::info(logs::boot, "CPU Vendor: %s", cpu.vendor_id());
|
|
||||||
log::info(logs::boot, "CPU Family %x Model %x Stepping %x",
|
|
||||||
cpu.family(), cpu.model(), cpu.stepping());
|
|
||||||
|
|
||||||
auto r = cpu.get(0x15);
|
auto r = cpu.get(0x15);
|
||||||
log::info(logs::boot, "CPU Crystal: %dHz", r.ecx);
|
log::info(logs::boot, "CPU Crystal: %dHz", r.ecx);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user