diff --git a/src/kernel/cpu.cpp b/src/kernel/cpu.cpp index a7862df..b84f80d 100644 --- a/src/kernel/cpu.cpp +++ b/src/kernel/cpu.cpp @@ -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(&m_vendor_id[0]), reinterpret_cast(&m_vendor_id[8]), reinterpret_cast(&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(&m_brand_name[0]), + reinterpret_cast(&m_brand_name[4]), + reinterpret_cast(&m_brand_name[8]), + reinterpret_cast(&m_brand_name[12])); + __cpuid(cpuid_extended + 3, 0, + reinterpret_cast(&m_brand_name[16]), + reinterpret_cast(&m_brand_name[20]), + reinterpret_cast(&m_brand_name[24]), + reinterpret_cast(&m_brand_name[28])); + __cpuid(cpuid_extended + 4, 0, + reinterpret_cast(&m_brand_name[32]), + reinterpret_cast(&m_brand_name[36]), + reinterpret_cast(&m_brand_name[40]), + reinterpret_cast(&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(cpu_feature::name)); \ + log::debug(logs::boot, " Supports %9s: %s", #name, (r.regname & (1<(feat))) != 0; +} diff --git a/src/kernel/cpu.h b/src/kernel/cpu.h index dd48549..89bc528 100644 --- a/src/kernel/cpu.h +++ b/src/kernel/cpu.h @@ -23,40 +23,69 @@ struct 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 { public: + /// CPUID result register values struct regs { - uint32_t eax, ebx, ecx, edx; - - regs() : eax(0), ebx(0), ecx(0), edx(0) {} - 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) {} + union { + uint32_t reg[4]; + uint32_t eax, ebx, ecx, edx; + }; + /// Return true if bit |bit| of EAX is set 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; } + + /// Return true if bit |bit| of ECX is set 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; } }; 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; + /// Get the name of the cpu vendor (eg, "GenuineIntel") 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; } - inline uint16_t family() const { return m_family; } - inline uint16_t model() const { return m_model; } + + /// Get the brand name of this processor model + inline const char * brand_name() const { return m_brand_name; } + + /// 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: - void read(); - - uint32_t m_high_leaf; + uint32_t m_high_basic; + uint32_t m_high_ext; char m_vendor_id[13]; - - uint8_t m_cpu_type; - uint8_t m_stepping; - uint16_t m_family; - uint16_t m_model; + char m_brand_name[48]; + uint64_t m_features; }; diff --git a/src/kernel/cpu_features.inc b/src/kernel/cpu_features.inc new file mode 100644 index 0000000..7aab6ea --- /dev/null +++ b/src/kernel/cpu_features.inc @@ -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) diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 565f07b..a2a1516 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -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, "Runtime service is at: %016lx", header->runtime); + cpu_id cpu; + cpu.validate(); + initrd::disk ird(header->initrd, heap); log::info(logs::boot, "initrd loaded with %d files.", ird.files().count()); for (auto &f : ird.files()) @@ -94,11 +97,6 @@ kernel_main(kernel_args *header) 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); log::info(logs::boot, "CPU Crystal: %dHz", r.ecx);