[cpu] Reimplement CPUID features as util::bitset

The cpu::cpu_id class no longer looks up all known features in the
constructor, but instead provides access to the map of supported
features as a bitset from the verify() method. It also exposes the
brand_name() method instead of loading the brand name string in the
constructor and storing it as part of the object.
This commit is contained in:
Justin C. Miller
2022-03-13 17:33:16 -07:00
parent e2eaf43b4a
commit 54aef00913
6 changed files with 69 additions and 59 deletions

View File

@@ -1,5 +1,6 @@
#include <cpu/cpu_id.h>
#include "console.h" #include "console.h"
#include "cpu/cpu_id.h"
#include "error.h" #include "error.h"
#include "hardware.h" #include "hardware.h"
#include "status.h" #include "status.h"
@@ -83,19 +84,21 @@ check_cpu_supported()
status_line status {L"Checking CPU features"}; status_line status {L"Checking CPU features"};
cpu::cpu_id cpu; cpu::cpu_id cpu;
uint64_t missing = cpu.missing(); cpu::cpu_id::features features = cpu.validate();
if (missing) { bool supported = true;
#define CPU_FEATURE_OPT(...) #define CPU_FEATURE_OPT(...)
#define CPU_FEATURE_REQ(name, ...) \ #define CPU_FEATURE_REQ(name, ...) \
if (!cpu.has_feature(cpu::feature::name)) { \ if (!features[cpu::feature::name]) { \
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \ status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
supported = false; \
} }
#include "cpu/features.inc" #include "cpu/features.inc"
#undef CPU_FEATURE_REQ #undef CPU_FEATURE_REQ
#undef CPU_FEATURE_OPT #undef CPU_FEATURE_OPT
if (!supported)
error::raise(uefi::status::unsupported, L"CPU not supported"); error::raise(uefi::status::unsupported, L"CPU not supported");
}
} }

View File

@@ -1,6 +1,7 @@
#include <new> #include <new>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <util/bitset.h>
#include "assert.h" #include "assert.h"
#include "cpu.h" #include "cpu.h"
@@ -30,18 +31,23 @@ cpu_validate()
{ {
cpu::cpu_id cpu; cpu::cpu_id cpu;
log::info(logs::boot, "CPU: %s", cpu.brand_name()); char brand_name[50];
cpu.brand_name(brand_name);
cpu::cpu_id::features features = cpu.validate();
log::info(logs::boot, "CPU: %s", brand_name);
log::debug(logs::boot, " Vendor is %s", cpu.vendor_id()); log::debug(logs::boot, " Vendor is %s", cpu.vendor_id());
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic()); 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); log::debug(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended);
#define CPU_FEATURE_OPT(name, ...) \ #define CPU_FEATURE_OPT(name, ...) \
log::debug(logs::boot, " Supports %9s: %s", #name, cpu.has_feature(cpu::feature::name) ? "yes" : "no"); log::debug(logs::boot, " Supports %9s: %s", #name, features[cpu::feature::name] ? "yes" : "no");
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \ #define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \ log::debug(logs::boot, " Supports %9s: %s", #name, features[cpu::feature::name] ? "yes" : "no"); \
kassert(cpu.has_feature(cpu::feature::name), "Missing required CPU feature " #name ); kassert(features[cpu::feature::name], "Missing required CPU feature " #name );
#include "cpu/features.inc" #include "cpu/features.inc"
#undef CPU_FEATURE_OPT #undef CPU_FEATURE_OPT

View File

@@ -2,6 +2,7 @@
module("cpu", module("cpu",
kind = "lib", kind = "lib",
deps = [ "util" ],
sources = [ sources = [
"cpu_id.cpp", "cpu_id.cpp",
], ],

View File

@@ -2,6 +2,7 @@
/// \file cpu_id.h Definition of required cpu features for jsix /// \file cpu_id.h Definition of required cpu features for jsix
#include <stdint.h> #include <stdint.h>
#include <util/bitset.h>
namespace cpu { namespace cpu {
@@ -18,6 +19,7 @@ enum class feature {
class cpu_id class cpu_id
{ {
public: public:
using features = util::bitset<(unsigned)feature::max>;
static constexpr uint32_t cpuid_extended = 0x80000000; static constexpr uint32_t cpuid_extended = 0x80000000;
/// CPUID result register values /// CPUID result register values
@@ -42,6 +44,10 @@ public:
cpu_id(); cpu_id();
/// Check which of the cpu::feature flags this CPU supports.
/// \returns A util::bitset mapping to cpu::feature values
features validate() const;
/// The the result of a given CPUID leaf/subleaf /// The the result of a given CPUID leaf/subleaf
/// \arg leaf The leaf selector (initial EAX) /// \arg leaf The leaf selector (initial EAX)
/// \arg subleaf The subleaf selector (initial ECX) /// \arg subleaf The subleaf selector (initial ECX)
@@ -54,8 +60,11 @@ public:
/// Get the name of the cpu vendor (eg, "GenuineIntel") /// 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; }
/// Get the brand name of this processor model /// Put the brand name of this processor model into the
inline const char * brand_name() const { return m_brand_name; } /// provided buffer.
/// \arg buffer Pointer to a buffer at least 48 bytes long
/// \returns True if the brand name was obtained
bool brand_name(char *buffer) const;
/// Get the highest basic CPUID leaf supported /// Get the highest basic CPUID leaf supported
inline uint32_t highest_basic() const { return m_high_basic; } inline uint32_t highest_basic() const { return m_high_basic; }
@@ -63,22 +72,10 @@ public:
/// Get the highest extended CPUID leaf supported /// Get the highest extended CPUID leaf supported
inline uint32_t highest_ext() const { return m_high_ext; } 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: private:
uint32_t m_high_basic; uint32_t m_high_basic;
uint32_t m_high_ext; uint32_t m_high_ext;
uint64_t m_features;
uint64_t m_missing;
char m_vendor_id[13]; char m_vendor_id[13];
char m_brand_name[48];
}; };
} }

View File

@@ -23,9 +23,7 @@ __cpuid(
if (edx) *edx = d; if (edx) *edx = d;
} }
cpu_id::cpu_id() : cpu_id::cpu_id()
m_features {0},
m_missing {0}
{ {
__cpuid(0, 0, __cpuid(0, 0,
&m_high_basic, &m_high_basic,
@@ -34,46 +32,31 @@ cpu_id::cpu_id() :
reinterpret_cast<uint32_t *>(&m_vendor_id[4])); reinterpret_cast<uint32_t *>(&m_vendor_id[4]));
__cpuid(cpuid_extended, 0, &m_high_ext); __cpuid(cpuid_extended, 0, &m_high_ext);
}
if (m_high_ext >= cpuid_extended + 4) { cpu_id::features
__cpuid(cpuid_extended + 2, 0, cpu_id::validate() const
reinterpret_cast<uint32_t *>(&m_brand_name[0]), {
reinterpret_cast<uint32_t *>(&m_brand_name[4]), cpu_id::features feats;
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;
}
uint32_t leaf = -1u; uint32_t leaf = -1u;
uint32_t sub = -1u; uint32_t sub = -1u;
regs r; regs r;
#define CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit) \ #define CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit) \
if (leaf != feat_leaf || sub != feat_sub) { \ if (leaf != feat_leaf || sub != feat_sub) { \
leaf = feat_leaf; sub = feat_sub; r = get(leaf, sub); \ leaf = feat_leaf; sub = feat_sub; r = get(leaf, sub); \
} \ } \
if (r.regname & (1ull << bit)) \ if (r.regname & (1ull << bit)) \
m_features |= (1ull << static_cast<uint64_t>(feature::name)); \ feats.set(feature::name);
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \ #define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
CPU_FEATURE_OPT(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<uint64_t>(feature::name)); \
}
#include "cpu/features.inc" #include "cpu/features.inc"
#undef CPU_FEATURE_OPT #undef CPU_FEATURE_OPT
#undef CPU_FEATURE_REQ #undef CPU_FEATURE_REQ
return feats;
} }
cpu_id::regs cpu_id::regs
@@ -88,12 +71,6 @@ cpu_id::get(uint32_t leaf, uint32_t sub) const
return ret; return ret;
} }
bool
cpu_id::has_feature(feature feat)
{
return (m_features & (1 << static_cast<uint64_t>(feat))) != 0;
}
uint8_t uint8_t
cpu_id::local_apic_id() const cpu_id::local_apic_id() const
{ {
@@ -103,4 +80,29 @@ cpu_id::local_apic_id() const
return static_cast<uint8_t>(ebx >> 24); return static_cast<uint8_t>(ebx >> 24);
} }
bool
cpu_id::brand_name(char *buffer) const
{
if (m_high_ext < cpuid_extended + 4)
return false;
__cpuid(cpuid_extended + 2, 0,
reinterpret_cast<uint32_t *>(&buffer[0]),
reinterpret_cast<uint32_t *>(&buffer[4]),
reinterpret_cast<uint32_t *>(&buffer[8]),
reinterpret_cast<uint32_t *>(&buffer[12]));
__cpuid(cpuid_extended + 3, 0,
reinterpret_cast<uint32_t *>(&buffer[16]),
reinterpret_cast<uint32_t *>(&buffer[20]),
reinterpret_cast<uint32_t *>(&buffer[24]),
reinterpret_cast<uint32_t *>(&buffer[28]));
__cpuid(cpuid_extended + 4, 0,
reinterpret_cast<uint32_t *>(&buffer[32]),
reinterpret_cast<uint32_t *>(&buffer[36]),
reinterpret_cast<uint32_t *>(&buffer[40]),
reinterpret_cast<uint32_t *>(&buffer[44]));
return true;
}
} }

View File

@@ -9,6 +9,7 @@ module("util",
public_headers = [ public_headers = [
"util/basic_types.h", "util/basic_types.h",
"util/bip_buffer.h", "util/bip_buffer.h",
"util/bitset.h",
"util/counted.h", "util/counted.h",
"util/deque.h", "util/deque.h",
"util/enum_bitfields.h", "util/enum_bitfields.h",