|
17 | 17 |
|
18 | 18 | #include <kernel/cpuid.hpp> |
19 | 19 | #include <cstring> |
| 20 | +#include <cstdint> |
| 21 | +#include <array> |
20 | 22 |
|
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; |
106 | 142 |
|
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); |
109 | 190 | 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; |
113 | 194 | } |
114 | 195 |
|
115 | | -bool CPUID::isIntelCpu() { |
116 | | - cpuid_t info = cpuid_info(0, 0); |
| 196 | +bool CPUID::isIntelCpu() |
| 197 | +{ |
| 198 | + auto result = cpuid(0, 0); |
117 | 199 | 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; |
121 | 203 | } |
122 | 204 |
|
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); |
127 | 209 |
|
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 | + } |
130 | 217 | } |
0 commit comments