Skip to content

Commit 04ca28a

Browse files
authored
Merge pull request #861 from gurka/cpuid
kernel: draft of cpuid rewrite
2 parents 1664c2e + bd60272 commit 04ca28a

2 files changed

Lines changed: 269 additions & 106 deletions

File tree

api/kernel/cpuid.hpp

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,86 @@
1919
#ifndef KERNEL_CPUID_HPP
2020
#define KERNEL_CPUID_HPP
2121

22-
struct CPUID {
23-
static bool isAmdCpu();
24-
static bool isIntelCpu();
25-
static bool hasRDRAND();
26-
}; //< CPUID
22+
namespace CPUID
23+
{
24+
enum class Feature
25+
{
26+
// ------------------------------------------------------------------------
27+
// Processor Info and Feature Bits
28+
// ------------------------------------------------------------------------
29+
SSE3, // Streaming SIMD Extensions 3
30+
PCLMULQDQ, // PCLMULQDQ Instruction
31+
DTES64, // 64-Bit Debug Store Area
32+
MONITOR, // MONITOR/MWAIT
33+
DS_CPL, // CPL Qualified Debug Store
34+
VMX, // Virtual Machine Extensions
35+
SMX, // Safer Mode Extensions
36+
EST, // Enhanced SpeedStep Technology
37+
TM2, // Thermal Monitor 2
38+
SSSE3, // Supplemental Streaming SIMD Extensions 3
39+
CNXT_ID, // L1 Context ID
40+
FMA, // Fused Multiply Add
41+
CX16, // CMPXCHG16B Instruction
42+
XTPR, // xTPR Update Control
43+
PDCM, // Perf/Debug Capability MSR
44+
PCID, // Process-context Identifiers
45+
DCA, // Direct Cache Access
46+
SSE4_1, // Streaming SIMD Extensions 4.1
47+
SSE4_2, // Streaming SIMD Extensions 4.2
48+
X2APIC, // Extended xAPIC Support
49+
MOVBE, // MOVBE Instruction
50+
POPCNT, // POPCNT Instruction
51+
TSC_DEADLINE, // Local APIC supports TSC Deadline
52+
AES, // AESNI Instruction
53+
XSAVE, // XSAVE/XSTOR States
54+
OSXSAVE, // OS Enabled Extended State Management
55+
AVX, // AVX Instructions
56+
F16C, // 16-bit Floating Point Instructions
57+
RDRAND, // RDRAND Instruction
58+
59+
FPU, // Floating-Point Unit On-Chip
60+
VME, // Virtual 8086 Mode Extensions
61+
DE, // Debugging Extensions
62+
PSE, // Page Size Extension
63+
TSC, // Time Stamp Counter
64+
MSR, // Model Specific Registers
65+
PAE, // Physical Address Extension
66+
MCE, // Machine-Check Exception
67+
CX8, // CMPXCHG8 Instruction
68+
APIC, // APIC On-Chip
69+
SEP, // SYSENTER/SYSEXIT instructions
70+
MTRR, // Memory Type Range Registers
71+
PGE, // Page Global Bit
72+
MCA, // Machine-Check Architecture
73+
CMOV, // Conditional Move Instruction
74+
PAT, // Page Attribute Table
75+
PSE_36, // 36-bit Page Size Extension
76+
PSN, // Processor Serial Number
77+
CLFLUSH, // CLFLUSH Instruction
78+
DS, // Debug Store
79+
ACPI, // Thermal Monitor and Software Clock Facilities
80+
MMX, // MMX Technology
81+
FXSR, // FXSAVE and FXSTOR Instructions
82+
SSE, // Streaming SIMD Extensions
83+
SSE2, // Streaming SIMD Extensions 2
84+
SS, // Self Snoop
85+
HTT, // Multi-Threading
86+
TM, // Thermal Monitor
87+
PBE, // Pending Break Enable
88+
89+
// ------------------------------------------------------------------------
90+
// Extended Processor Info and Feature Bits (not complete)
91+
// ------------------------------------------------------------------------
92+
SYSCALL, // SYSCALL/SYSRET
93+
NX, // Execute Disable Bit
94+
PDPE1GB, // 1 GB Pages
95+
RDTSCP, // RDTSCP and IA32_TSC_AUX
96+
LM, // Long mode (64-bit Architecture)
97+
};
98+
99+
bool isAmdCpu();
100+
bool isIntelCpu();
101+
bool hasFeature(Feature f);
102+
} //< CPUID
27103

28104
#endif //< KERNEL_CPUID_HPP

src/kernel/cpuid.cpp

Lines changed: 188 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -17,114 +17,201 @@
1717

1818
#include <kernel/cpuid.hpp>
1919
#include <cstring>
20+
#include <cstdint>
21+
#include <array>
2022

21-
#define ECX_SSE3 (1 << 0) // Streaming SIMD Extensions 3
22-
#define ECX_PCLMULQDQ (1 << 1) // PCLMULQDQ Instruction
23-
#define ECX_DTES64 (1 << 2) // 64-Bit Debug Store Area
24-
#define ECX_MONITOR (1 << 3) // MONITOR/MWAIT
25-
#define ECX_DS_CPL (1 << 4) // CPL Qualified Debug Store
26-
#define ECX_VMX (1 << 5) // Virtual Machine Extensions
27-
#define ECX_SMX (1 << 6) // Safer Mode Extensions
28-
#define ECX_EST (1 << 7) // Enhanced SpeedStep Technology
29-
#define ECX_TM2 (1 << 8) // Thermal Monitor 2
30-
#define ECX_SSSE3 (1 << 9) // Supplemental Streaming SIMD Extensions 3
31-
#define ECX_CNXT_ID (1 << 10) // L1 Context ID
32-
#define ECX_FMA (1 << 12) // Fused Multiply Add
33-
#define ECX_CX16 (1 << 13) // CMPXCHG16B Instruction
34-
#define ECX_XTPR (1 << 14) // xTPR Update Control
35-
#define ECX_PDCM (1 << 15) // Perf/Debug Capability MSR
36-
#define ECX_PCID (1 << 17) // Process-context Identifiers
37-
#define ECX_DCA (1 << 18) // Direct Cache Access
38-
#define ECX_SSE41 (1 << 19) // Streaming SIMD Extensions 4.1
39-
#define ECX_SSE42 (1 << 20) // Streaming SIMD Extensions 4.2
40-
#define ECX_X2APIC (1 << 21) // Extended xAPIC Support
41-
#define ECX_MOVBE (1 << 22) // MOVBE Instruction
42-
#define ECX_POPCNT (1 << 23) // POPCNT Instruction
43-
#define ECX_TSC (1 << 24) // Local APIC supports TSC Deadline
44-
#define ECX_AESNI (1 << 25) // AESNI Instruction
45-
#define ECX_XSAVE (1 << 26) // XSAVE/XSTOR States
46-
#define ECX_OSXSAVE (1 << 27) // OS Enabled Extended State Management
47-
#define ECX_AVX (1 << 28) // AVX Instructions
48-
#define ECX_F16C (1 << 29) // 16-bit Floating Point Instructions
49-
#define ECX_RDRAND (1 << 30) // RDRAND Instruction
50-
51-
#define EDX_FPU (1 << 0) // Floating-Point Unit On-Chip
52-
#define EDX_VME (1 << 1) // Virtual 8086 Mode Extensions
53-
#define EDX_DE (1 << 2) // Debugging Extensions
54-
#define EDX_PSE (1 << 3) // Page Size Extension
55-
#define EDX_TSC (1 << 4) // Time Stamp Counter
56-
#define EDX_MSR (1 << 5) // Model Specific Registers
57-
#define EDX_PAE (1 << 6) // Physical Address Extension
58-
#define EDX_MCE (1 << 7) // Machine-Check Exception
59-
#define EDX_CX8 (1 << 8) // CMPXCHG8 Instruction
60-
#define EDX_APIC (1 << 9) // APIC On-Chip
61-
#define EDX_SEP (1 << 11) // SYSENTER/SYSEXIT instructions
62-
#define EDX_MTRR (1 << 12) // Memory Type Range Registers
63-
#define EDX_PGE (1 << 13) // Page Global Bit
64-
#define EDX_MCA (1 << 14) // Machine-Check Architecture
65-
#define EDX_CMOV (1 << 15) // Conditional Move Instruction
66-
#define EDX_PAT (1 << 16) // Page Attribute Table
67-
#define EDX_PSE36 (1 << 17) // 36-bit Page Size Extension
68-
#define EDX_PSN (1 << 18) // Processor Serial Number
69-
#define EDX_CLFLUSH (1 << 19) // CLFLUSH Instruction
70-
#define EDX_DS (1 << 21) // Debug Store
71-
#define EDX_ACPI (1 << 22) // Thermal Monitor and Software Clock Facilities
72-
#define EDX_MMX (1 << 23) // MMX Technology
73-
#define EDX_FXSR (1 << 24) // FXSAVE and FXSTOR Instructions
74-
#define EDX_SSE (1 << 25) // Streaming SIMD Extensions
75-
#define EDX_SSE2 (1 << 26) // Streaming SIMD Extensions 2
76-
#define EDX_SS (1 << 27) // Self Snoop
77-
#define EDX_HTT (1 << 28) // Multi-Threading
78-
#define EDX_TM (1 << 29) // Thermal Monitor
79-
#define EDX_PBE (1 << 31) // Pending Break Enable
80-
81-
// ------------------------------------------------------------------------------------------------
82-
// Extended Function 0x01
83-
84-
#define EDX_SYSCALL (1 << 11) // SYSCALL/SYSRET
85-
#define EDX_XD (1 << 20) // Execute Disable Bit
86-
#define EDX_1GB_PAGE (1 << 26) // 1 GB Pages
87-
#define EDX_RDTSCP (1 << 27) // RDTSCP and IA32_TSC_AUX
88-
#define EDX_64_BIT (1 << 29) // 64-bit Architecture
89-
90-
struct cpuid_t {
91-
unsigned int EAX;
92-
unsigned int EBX;
93-
unsigned int ECX;
94-
unsigned int EDX;
95-
}; //< cpuid_t
96-
97-
// EBX/RBX needs to be preserved depending on the memory model and use of PIC
98-
static cpuid_t
99-
cpuid_info(unsigned func, unsigned subfunc) {
100-
cpuid_t info;
101-
asm volatile ("cpuid"
102-
: "=a"(info.EAX), "=b"(info.EBX), "=c"(info.ECX), "=d"(info.EDX)
103-
: "a"(func), "c"(subfunc) : "%eax", "%ebx", "%ecx", "%edx");
104-
return info;
105-
}
23+
namespace
24+
{
25+
using namespace CPUID;
26+
27+
enum class Register { EAX, EBX, ECX, EDX };
28+
29+
struct FeatureInfo
30+
{
31+
// Input when calling cpuid (eax and ecx)
32+
const uint32_t func;
33+
const uint32_t subfunc;
34+
35+
// Register and bit that holds the result
36+
const Register register_;
37+
const uint32_t bitmask;
38+
};
39+
40+
constexpr auto get_feature_info(Feature f)
41+
{
42+
using Register::EAX;
43+
using Register::EBX;
44+
using Register::ECX;
45+
using Register::EDX;
46+
47+
// Use switch-case so that the we get compiler warnings if
48+
// we forget to add information for features that we have declared
49+
switch (f)
50+
{
51+
// ----------------------------------------------------------------------
52+
// EAX=1: Processor Info and Feature Bits
53+
// ----------------------------------------------------------------------
54+
case Feature::SSE3: return FeatureInfo { 1, 0, ECX, 1u << 0 }; // Streaming SIMD Extensions 3
55+
case Feature::PCLMULQDQ: return FeatureInfo { 1, 0, ECX, 2u << 0 }; // PCLMULQDQ Instruction
56+
case Feature::DTES64: return FeatureInfo { 1, 0, ECX, 1u << 2 }; // 64-Bit Debug Store Area
57+
case Feature::MONITOR: return FeatureInfo { 1, 0, ECX, 1u << 3 }; // MONITOR/MWAIT
58+
case Feature::DS_CPL: return FeatureInfo { 1, 0, ECX, 1u << 4 }; // CPL Qualified Debug Store
59+
case Feature::VMX: return FeatureInfo { 1, 0, ECX, 1u << 5 }; // Virtual Machine Extensions
60+
case Feature::SMX: return FeatureInfo { 1, 0, ECX, 1u << 6 }; // Safer Mode Extensions
61+
case Feature::EST: return FeatureInfo { 1, 0, ECX, 1u << 7 }; // Enhanced SpeedStep Technology
62+
case Feature::TM2: return FeatureInfo { 1, 0, ECX, 1u << 8 }; // Thermal Monitor 2
63+
case Feature::SSSE3: return FeatureInfo { 1, 0, ECX, 1u << 9 }; // Supplemental Streaming SIMD Extensions 3
64+
case Feature::CNXT_ID: return FeatureInfo { 1, 0, ECX, 1u << 10 }; // L1 Context ID
65+
case Feature::FMA: return FeatureInfo { 1, 0, ECX, 1u << 12 }; // Fused Multiply Add
66+
case Feature::CX16: return FeatureInfo { 1, 0, ECX, 1u << 13 }; // CMPXCHG16B Instruction
67+
case Feature::XTPR: return FeatureInfo { 1, 0, ECX, 1u << 14 }; // xTPR Update Control
68+
case Feature::PDCM: return FeatureInfo { 1, 0, ECX, 1u << 15 }; // Perf/Debug Capability MSR
69+
case Feature::PCID: return FeatureInfo { 1, 0, ECX, 1u << 17 }; // Process-context Identifiers
70+
case Feature::DCA: return FeatureInfo { 1, 0, ECX, 1u << 18 }; // Direct Cache Access
71+
case Feature::SSE4_1: return FeatureInfo { 1, 0, ECX, 1u << 19 }; // Streaming SIMD Extensions 4.1
72+
case Feature::SSE4_2: return FeatureInfo { 1, 0, ECX, 1u << 20 }; // Streaming SIMD Extensions 4.2
73+
case Feature::X2APIC: return FeatureInfo { 1, 0, ECX, 1u << 21 }; // Extended xAPIC Support
74+
case Feature::MOVBE: return FeatureInfo { 1, 0, ECX, 1u << 22 }; // MOVBE Instruction
75+
case Feature::POPCNT: return FeatureInfo { 1, 0, ECX, 1u << 23 }; // POPCNT Instruction
76+
case Feature::TSC_DEADLINE: return FeatureInfo { 1, 0, ECX, 1u << 24 }; // Local APIC supports TSC Deadline
77+
case Feature::AES: return FeatureInfo { 1, 0, ECX, 1u << 25 }; // AESNI Instruction
78+
case Feature::XSAVE: return FeatureInfo { 1, 0, ECX, 1u << 26 }; // XSAVE/XSTOR States
79+
case Feature::OSXSAVE: return FeatureInfo { 1, 0, ECX, 1u << 27 }; // OS Enabled Extended State Management
80+
case Feature::AVX: return FeatureInfo { 1, 0, ECX, 1u << 28 }; // AVX Instructions
81+
case Feature::F16C: return FeatureInfo { 1, 0, ECX, 1u << 29 }; // 16-bit Floating Point Instructions
82+
case Feature::RDRAND: return FeatureInfo { 1, 0, ECX, 1u << 30 }; // RDRAND Instruction
83+
84+
case Feature::FPU: return FeatureInfo { 1, 0, EDX, 1u << 0 }; // Floating-Point Unit On-Chip
85+
case Feature::VME: return FeatureInfo { 1, 0, EDX, 1u << 1 }; // Virtual 8086 Mode Extensions
86+
case Feature::DE: return FeatureInfo { 1, 0, EDX, 1u << 2 }; // Debugging Extensions
87+
case Feature::PSE: return FeatureInfo { 1, 0, EDX, 1u << 3 }; // Page Size Extension
88+
case Feature::TSC: return FeatureInfo { 1, 0, EDX, 1u << 4 }; // Time Stamp Counter
89+
case Feature::MSR: return FeatureInfo { 1, 0, EDX, 1u << 5 }; // Model Specific Registers
90+
case Feature::PAE: return FeatureInfo { 1, 0, EDX, 1u << 6 }; // Physical Address Extension
91+
case Feature::MCE: return FeatureInfo { 1, 0, EDX, 1u << 7 }; // Machine-Check Exception
92+
case Feature::CX8: return FeatureInfo { 1, 0, EDX, 1u << 8 }; // CMPXCHG8 Instruction
93+
case Feature::APIC: return FeatureInfo { 1, 0, EDX, 1u << 9 }; // APIC On-Chip
94+
case Feature::SEP: return FeatureInfo { 1, 0, EDX, 1u << 11 }; // SYSENTER/SYSEXIT instructions
95+
case Feature::MTRR: return FeatureInfo { 1, 0, EDX, 1u << 12 }; // Memory Type Range Registers
96+
case Feature::PGE: return FeatureInfo { 1, 0, EDX, 1u << 13 }; // Page Global Bit
97+
case Feature::MCA: return FeatureInfo { 1, 0, EDX, 1u << 14 }; // Machine-Check Architecture
98+
case Feature::CMOV: return FeatureInfo { 1, 0, EDX, 1u << 15 }; // Conditional Move Instruction
99+
case Feature::PAT: return FeatureInfo { 1, 0, EDX, 1u << 16 }; // Page Attribute Table
100+
case Feature::PSE_36: return FeatureInfo { 1, 0, EDX, 1u << 17 }; // 36-bit Page Size Extension
101+
case Feature::PSN: return FeatureInfo { 1, 0, EDX, 1u << 18 }; // Processor Serial Number
102+
case Feature::CLFLUSH: return FeatureInfo { 1, 0, EDX, 1u << 19 }; // CLFLUSH Instruction
103+
case Feature::DS: return FeatureInfo { 1, 0, EDX, 1u << 21 }; // Debug Store
104+
case Feature::ACPI: return FeatureInfo { 1, 0, EDX, 1u << 22 }; // Thermal Monitor and Software Clock Facilities
105+
case Feature::MMX: return FeatureInfo { 1, 0, EDX, 1u << 23 }; // MMX Technology
106+
case Feature::FXSR: return FeatureInfo { 1, 0, EDX, 1u << 24 }; // FXSAVE and FXSTOR Instructions
107+
case Feature::SSE: return FeatureInfo { 1, 0, EDX, 1u << 25 }; // Streaming SIMD Extensions
108+
case Feature::SSE2: return FeatureInfo { 1, 0, EDX, 1u << 26 }; // Streaming SIMD Extensions 2
109+
case Feature::SS: return FeatureInfo { 1, 0, EDX, 1u << 27 }; // Self Snoop
110+
case Feature::HTT: return FeatureInfo { 1, 0, EDX, 1u << 28 }; // Multi-Threading
111+
case Feature::TM: return FeatureInfo { 1, 0, EDX, 1u << 29 }; // Thermal Monitor
112+
case Feature::PBE: return FeatureInfo { 1, 0, EDX, 1u << 31 }; // Pending Break Enable
113+
114+
// ----------------------------------------------------------------------
115+
// EAX=80000001h: Extended Processor Info and Feature Bits (not complete)
116+
// ----------------------------------------------------------------------
117+
case Feature::SYSCALL: return FeatureInfo { 0x80000001, 0, EDX, 1u << 11 }; // SYSCALL/SYSRET
118+
case Feature::NX: return FeatureInfo { 0x80000001, 0, EDX, 1u << 20 }; // Execute Disable Bit
119+
case Feature::PDPE1GB: return FeatureInfo { 0x80000001, 0, EDX, 1u << 26 }; // 1 GB Pages
120+
case Feature::RDTSCP: return FeatureInfo { 0x80000001, 0, EDX, 1u << 27 }; // RDTSCP and IA32_TSC_AUX
121+
case Feature::LM: return FeatureInfo { 0x80000001, 0, EDX, 1u << 29 }; // 64-bit Architecture
122+
}
123+
}
124+
125+
// Holds results of call to cpuid
126+
struct cpuid_t
127+
{
128+
uint32_t EAX;
129+
uint32_t EBX;
130+
uint32_t ECX;
131+
uint32_t EDX;
132+
}; //< cpuid_t
133+
134+
cpuid_t cpuid(uint32_t func, uint32_t subfunc)
135+
{
136+
// Cache up to 4 results
137+
struct cpuid_cache_t
138+
{
139+
// cpuid input
140+
uint32_t func;
141+
uint32_t subfunc;
106142

107-
bool CPUID::isAmdCpu() {
108-
cpuid_t info = cpuid_info(0, 0);
143+
// cpuid output
144+
cpuid_t result;
145+
146+
// valid or not in cache
147+
bool valid;
148+
};
149+
static std::array<cpuid_cache_t, 4> cache = {};
150+
151+
// Check cache for cpuid result
152+
for (const auto& cached : cache)
153+
{
154+
if (cached.valid &&
155+
cached.func == func &&
156+
cached.subfunc == subfunc)
157+
{
158+
return cached.result;
159+
}
160+
}
161+
162+
// Call cpuid
163+
// EBX/RBX needs to be preserved depending on the memory model and use of PIC
164+
cpuid_t result;
165+
asm volatile ("cpuid"
166+
: "=a"(result.EAX), "=b"(result.EBX), "=c"(result.ECX), "=d"(result.EDX)
167+
: "a"(func), "c"(subfunc) : "%eax", "%ebx", "%ecx", "%edx");
168+
169+
// Try to find an empty spot in the cache
170+
for (auto& cached : cache)
171+
{
172+
if (!cached.valid)
173+
{
174+
cached.func = func;
175+
cached.subfunc = subfunc;
176+
cached.result = result;
177+
cached.valid = true;
178+
break;
179+
}
180+
}
181+
182+
return result;
183+
}
184+
185+
} //< namespace
186+
187+
bool CPUID::isAmdCpu()
188+
{
189+
auto result = cpuid(0, 0);
109190
return
110-
memcmp((char*) &info.EBX, "htuA", 4) == 0
111-
&& memcmp((char*) &info.EDX, "itne", 4) == 0
112-
&& memcmp((char*) &info.ECX, "DMAc", 4) == 0;
191+
memcmp(reinterpret_cast<char*>(&result.EBX), "htuA", 4) == 0
192+
&& memcmp(reinterpret_cast<char*>(&result.EDX), "itne", 4) == 0
193+
&& memcmp(reinterpret_cast<char*>(&result.ECX), "DMAc", 4) == 0;
113194
}
114195

115-
bool CPUID::isIntelCpu() {
116-
cpuid_t info = cpuid_info(0, 0);
196+
bool CPUID::isIntelCpu()
197+
{
198+
auto result = cpuid(0, 0);
117199
return
118-
memcmp((char*) &info.EBX, "Genu", 4) == 0
119-
&& memcmp((char*) &info.EDX, "ineI", 4) == 0
120-
&& memcmp((char*) &info.ECX, "ntel", 4) == 0;
200+
memcmp(reinterpret_cast<char*>(&result.EBX), "Genu", 4) == 0
201+
&& memcmp(reinterpret_cast<char*>(&result.EDX), "ineI", 4) == 0
202+
&& memcmp(reinterpret_cast<char*>(&result.ECX), "ntel", 4) == 0;
121203
}
122204

123-
bool CPUID::hasRDRAND() {
124-
if (!isAmdCpu() && !isIntelCpu()) {
125-
return false;
126-
}
205+
bool CPUID::hasFeature(Feature f)
206+
{
207+
const auto feature_info = get_feature_info(f);
208+
const auto cpuid_result = cpuid(feature_info.func, feature_info.subfunc);
127209

128-
cpuid_t info = cpuid_info(1, 0);
129-
return (info.ECX & ECX_RDRAND) != 0;
210+
switch (feature_info.register_)
211+
{
212+
case Register::EAX: return (cpuid_result.EAX & feature_info.bitmask) != 0;
213+
case Register::EBX: return (cpuid_result.EBX & feature_info.bitmask) != 0;
214+
case Register::ECX: return (cpuid_result.ECX & feature_info.bitmask) != 0;
215+
case Register::EDX: return (cpuid_result.EDX & feature_info.bitmask) != 0;
216+
}
130217
}

0 commit comments

Comments
 (0)