diff --git a/modules.yaml b/modules.yaml index ceff427..75bc913 100644 --- a/modules.yaml +++ b/modules.yaml @@ -6,6 +6,7 @@ modules: output: jsix.elf target: host deps: + - cpu - kutil includes: - src/kernel @@ -62,6 +63,8 @@ modules: kind: exe target: boot output: boot.efi + deps: + - cpu source: - src/boot/main.cpp - src/boot/console.cpp @@ -110,6 +113,14 @@ modules: - src/libraries/kutil/memory.cpp - src/libraries/kutil/printf.c + cpu: + kind: lib + output: libcpu.a + includes: + - src/libraries/cpu/include + source: + - src/libraries/cpu/cpu.cpp + libc: kind: lib diff --git a/src/boot/main.cpp b/src/boot/main.cpp index 3f74ee4..f48e3b8 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -8,6 +8,7 @@ #include #include "console.h" +#include "cpu/cpu.h" #include "error.h" #include "fs.h" #include "hardware.h" @@ -94,6 +95,28 @@ add_module(args::header *args, args::mod_type type, buffer &data) m.size = data.size; } +/// Check that all required cpu features are supported +void +check_cpu_supported() +{ + status_line status {L"Checking CPU features"}; + + cpu::cpu_id cpu; + uint64_t missing = cpu.missing(); + if (missing) { +#define CPU_FEATURE_OPT(...) +#define CPU_FEATURE_REQ(name, ...) \ + if (!cpu.has_feature(cpu::feature::name)) { \ + status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \ + } +#include "cpu/features.inc" +#undef CPU_FEATURE_REQ +#undef CPU_FEATURE_OPT + + error::raise(uefi::status::unsupported, L"CPU not supported"); + } +} + /// The main procedure for the portion of the loader that runs while /// UEFI is still in control of the machine. (ie, while the loader still /// has access to boot services. @@ -160,6 +183,7 @@ efi_main(uefi::handle image, uefi::system_table *st) { using namespace boot; console con(st->boot_services, st->con_out); + check_cpu_supported(); args::header *args = uefi_preboot(image, st); memory::efi_mem_map map = uefi_exit(args, image, st->boot_services); diff --git a/src/kernel/cpu.cpp b/src/kernel/cpu.cpp index 7e6c25b..4b0720e 100644 --- a/src/kernel/cpu.cpp +++ b/src/kernel/cpu.cpp @@ -1,116 +1,31 @@ #include +#include "kutil/assert.h" #include "kutil/memory.h" #include "cpu.h" +#include "cpu/cpu.h" #include "log.h" cpu_data bsp_cpu_data; -static constexpr uint32_t cpuid_extended = 0x80000000; - - -inline static void -__cpuid( - uint32_t leaf, - uint32_t subleaf, - uint32_t *eax, - uint32_t *ebx = nullptr, - uint32_t *ecx = nullptr, - uint32_t *edx = nullptr) -{ - uint32_t a, b, c, d; - __asm__ __volatile__ ( "cpuid" - : "=a"(a), "=b"(b), "=c"(c), "=d"(d) - : "a"(leaf), "c"(subleaf) - ); - if (eax) *eax = a; - if (ebx) *ebx = b; - if (ecx) *ecx = c; - if (edx) *edx = d; -} - -cpu_id::cpu_id() : - m_features(0) -{ - __cpuid(0, 0, - &m_high_basic, - reinterpret_cast(&m_vendor_id[0]), - reinterpret_cast(&m_vendor_id[8]), - reinterpret_cast(&m_vendor_id[4])); - - __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 -{ - 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; - - __cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx); - return ret; -} - void -cpu_id::validate() +cpu_validate() { - bool fail = false; - uint32_t leaf = 0; - uint32_t sub = 0; - regs r; + cpu::cpu_id cpu; - log::info(logs::boot, "CPU: %s", brand_name()); - log::debug(logs::boot, " Vendor is %s", vendor_id()); + log::info(logs::boot, "CPU: %s", cpu.brand_name()); + log::debug(logs::boot, " Vendor is %s", cpu.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); + log::debug(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic()); + log::debug(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::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 & (1ull << bit)) \ - m_features |= (1ull << static_cast(cpu_feature::name)); \ - log::debug(logs::boot, " Supports %9s: %s", #name, (r.regname & (1ull << bit)) ? "yes" : "no"); +#define CPU_FEATURE_OPT(name, ...) \ + log::debug(logs::boot, " Supports %9s: %s", #name, cpu.has_feature(cpu::feature::name) ? "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 & (1ull << bit)) == 0) { \ - log::error(logs::boot, "CPU missing required feature " #name); \ - fail = true; \ - } + kassert(cpu.has_feature(cpu::feature::name), "Missing required CPU feature " #name ); -#include "cpu_features.inc" +#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(feat))) != 0; } diff --git a/src/kernel/cpu.h b/src/kernel/cpu.h index 84108fd..ed5722d 100644 --- a/src/kernel/cpu.h +++ b/src/kernel/cpu.h @@ -27,69 +27,6 @@ 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 { - 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; } - - /// 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: - uint32_t m_high_basic; - uint32_t m_high_ext; - char m_vendor_id[13]; - char m_brand_name[48]; - uint64_t m_features; -}; +// We already validated the required options in the bootloader, +// but iterate the options and log about them. +void cpu_validate(); diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 120a2b2..e872f1d 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -135,8 +135,7 @@ kernel_main(args::header *header) run_constructors(); memory_initialize_post_ctors(header); - cpu_id cpu; - cpu.validate(); + cpu_validate(); for (size_t i = 0; i < header->num_modules; ++i) { args::module &mod = header->modules[i]; diff --git a/src/libraries/cpu/cpu.cpp b/src/libraries/cpu/cpu.cpp new file mode 100644 index 0000000..98395e5 --- /dev/null +++ b/src/libraries/cpu/cpu.cpp @@ -0,0 +1,97 @@ +#include +#include "cpu/cpu.h" + +namespace cpu { + +inline static void +__cpuid( + uint32_t leaf, + uint32_t subleaf, + uint32_t *eax, + uint32_t *ebx = nullptr, + uint32_t *ecx = nullptr, + uint32_t *edx = nullptr) +{ + uint32_t a, b, c, d; + __asm__ __volatile__ ( "cpuid" + : "=a"(a), "=b"(b), "=c"(c), "=d"(d) + : "a"(leaf), "c"(subleaf) + ); + if (eax) *eax = a; + if (ebx) *ebx = b; + if (ecx) *ecx = c; + if (edx) *edx = d; +} + +cpu_id::cpu_id() : + m_features {0}, + m_missing {0} +{ + __cpuid(0, 0, + &m_high_basic, + reinterpret_cast(&m_vendor_id[0]), + reinterpret_cast(&m_vendor_id[8]), + reinterpret_cast(&m_vendor_id[4])); + + __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; + } + + uint32_t leaf = -1u; + uint32_t sub = -1u; + regs r; +#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 & (1ull << bit)) \ + m_features |= (1ull << static_cast(feature::name)); \ + +#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \ + CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \ + if ((r.regname & (1ull << bit)) == 0) { \ + m_missing |= (1ull << static_cast(feature::name)); \ + } + +#include "cpu/features.inc" +#undef CPU_FEATURE_OPT +#undef CPU_FEATURE_REQ +} + +cpu_id::regs +cpu_id::get(uint32_t leaf, uint32_t sub) const +{ + 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; + + __cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx); + return ret; +} + +bool +cpu_id::has_feature(feature feat) +{ + return (m_features & (1 << static_cast(feat))) != 0; +} + +} diff --git a/src/libraries/cpu/include/cpu/cpu.h b/src/libraries/cpu/include/cpu/cpu.h new file mode 100644 index 0000000..ceace0f --- /dev/null +++ b/src/libraries/cpu/include/cpu/cpu.h @@ -0,0 +1,81 @@ +#pragma once +/// \file cpu.h Definition of required cpu features for jsix + +#include + +namespace cpu { + +/// Enum of the cpu features jsix cares about +enum class 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: + static constexpr uint32_t cpuid_extended = 0x80000000; + + /// CPUID result register values + struct regs { + 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; } + + /// 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; } + + /// Get which required options are missing as flags + inline uint64_t missing() const { return m_missing; } + + /// Validate the CPU supports the necessary options for jsix + inline bool supported() const { return m_missing; } + + /// Return true if the CPU claims to support the given feature + bool has_feature(feature feat); + +private: + uint32_t m_high_basic; + uint32_t m_high_ext; + uint64_t m_features; + uint64_t m_missing; + char m_vendor_id[13]; + char m_brand_name[48]; +}; + +} diff --git a/src/kernel/cpu_features.inc b/src/libraries/cpu/include/cpu/features.inc similarity index 100% rename from src/kernel/cpu_features.inc rename to src/libraries/cpu/include/cpu/features.inc