1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
//! Processor feature detection with CPUID.
use core::arch::asm;
use tartan_bitfield::bitfield;
/// Low-level CPUID call. Writes the leaf/subleaf index given in the arguments to EAX/ECX
/// and returns the values written to EAX–EDX.
pub fn cpuid(leaf_index: u32, subleaf_index: u32) -> [u32; 4] {
let mut result = [0_u32; 4];
unsafe {
asm!(
"
cpuid
mov {0:e}, ebx
",
out(reg) result[1],
inout("eax") leaf_index => result[0],
inout("ecx") subleaf_index => result[2],
out("edx") result[3],
);
}
result
}
/// Get the maximum primary index value (EAX) supported for *basic* CPUID calls by this
/// processor (below `0x8000_0000`).
pub fn max_cpuid_index_basic() -> u32 {
cpuid(0, 0)[0]
}
/// Get the maximum primary index value (EAX) supported for *extended* CPUID calls by this
/// processor (above `0x8000_0000`).
pub fn max_cpuid_index_extended() -> u32 {
cpuid(0x8000_0000, 0)[0]
}
bitfield! {
/// Primary feature list returned in `CPUID.01H:ECX+EDX`.
pub struct BasicFeatures(u64) {
// Bits 0..32 from EDX
/// `FPU`: The processor has a built-in x87 floating-point unit
[ 0] pub on_chip_fpu,
/// `VME`: Supports virtual real-mode extensions (VME) and protected-mode virtual
/// interrupts.
[ 1] pub virtual_8086_extensions,
/// `DE`: Supports breaking on I/O and on accessing debug registers `DR4`–`DR5`.
[ 2] pub debugging_extensions,
/// `PSE`: Supports 4MB virtual memory pages and the dirty flag.
[ 3] pub page_size_extension,
/// `TSC`: Supports reading the processor's timestamp with `RDTSC`.
[ 4] pub time_stamp_counter,
/// `MSR`: Has model-specific registers which can be accessed with
/// `RDMSR`/`WRMSR`.
[ 5] pub model_registers,
/// `PAE`: Supports mapping virtual memory to physical addresses longer than 32
/// bits.
[ 6] pub physical_address_extension,
/// `MCE`: Defines an exception (18) for reporting internal processor errors.
[ 7] pub machine_check_exception,
/// `CX8`: Supports the 64-byte `CMPXCHG8B` atomic instruction.
[ 8] pub compare_exchange_64bit,
/// `APIC`: The processor has a built-in advanced programmable interrupt
/// controller (APIC).
[ 9] pub on_chip_apic,
/// `SEP`: Supports the `SYSENTER`/`SYSEXIT` instructions.
[11] pub sysenter,
/// `MTRR`: Has memory type range registers.
[12] pub memory_type_range_registers,
/// `PGE`: Supports global pages, which are available in all task contexts
[13] pub global_pages,
/// `MCA`: Supports extended features for reporting internal processor errors.
[14] pub machine_check_architecture,
/// `CMOV`: Supports the `CMOV` instruction and `FCMOV`/`FCOMI` if FPU is present.
[15] pub conditional_move,
/// `PAT`: Supports page attribute tables.
[16] pub page_attribute_table,
/// `PSE-36`: Supports 4MB virtual memory pages that can map to physical addresses
/// longer than 32 bits.
[17] pub page_size_extension_36bit,
/// `PSN`: Supports retrieving a processor serial number with the CPUID
/// instruction.
[18] pub serial_number,
/// `CLFSH`: Supports flushing a cache line with the `CLFLUSH` instruction.
[19] pub cache_line_flush,
/// `DS`: Supports writing debug information to memory.
[21] pub debug_store,
/// `ACPI`: Supports thermal monitoring and power management with software.
[22] pub thermal_power_management,
/// `MMX`: Supports MMX instructions.
[23] pub mmx,
/// `FXSAVE`: Supports managing FPU state with `FXSAVE`/`FXRSTOR`.
[24] pub fpu_save,
/// `SSE`: Supports SSE instructions.
[25] pub sse,
/// `SSE2`: Supports SSE2 instructions.
[26] pub sse_2,
/// The processor can snoop on its own cache line. This helps deal with certain
/// memory issues.
[27] pub self_snoop,
/// `HTT`: Indicates that the number of reserved APIC IDs is available with the
/// CPUID instruction. If clear, only one ID is reserved.
[28] pub max_apic_id_field,
/// `TM`: Has thermal monitor control circuitry (TCC).
[29] pub thermal_monitor,
/// `PBE`: Supports a pin notifying a stopped processor that an interrupt is
/// pending.
[31] pub pending_break_enable,
// Bits 32..64 from ECX
/// `SSE3`: Supports SSE3 instructions.
[32] pub sse_3,
/// Supports carry-less multiplication of two 64-bit integers using the
/// `PCLMULQDQ` instruction.
[33] pub carryless_multiply_64bit,
/// `DTES64`: Supports 64-bit addresses for the debug store.
[34] pub debug_store_64bit,
/// `MONITOR`: Supports the `MONITOR`/`MWAIT` instructions.
[35] pub monitor,
/// `DS-CPL`: Supports saving the permission level with data written to the debug
/// store.
[36] pub permission_qualified_debug_store,
/// `VMX`: Supports virtual machine extensions.
[37] pub virtual_machine_extensions,
/// `SMX`: Supports safer-mode extensions
[38] pub safer_mode_extensions,
/// `EIST`: Supports enhanced SpeedStep throttling.
[39] pub enhanced_speedstep,
/// Supports the TM2 thermal monitor interface.
[40] pub thermal_monitor_2,
/// `SSSE3`: Supports Supplemental SSE3 (SSSE3) instructions.
[41] pub supplemental_sse_3,
/// `CNXT-ID`: Supports setting the L1 cache to adaptive or shared mode.
[42] pub l1_context_id,
/// `SDBG`: Supports an MSR for chip debugging.
[43] pub debug_interface_model_register,
/// `FMA`: Supports fused multiply-add SSE instructions.
[44] pub fused_multiply_add,
/// `CMPXCHG16B`: Supports the 128-bit `CMPXCHG16B` atomic instruction.
[45] pub compare_exchange_128bit,
/// Supports disabling xTPR task priority messages to the chipset through
/// `IA32_MISC_ENABLE[23]`.
[46] pub chipset_task_priority_control,
/// `PDCM`: Supports a model-specific register that lists performance-monitoring
/// and debug features.
[47] pub monitor_debug_capabilities_register,
/// `PCID`: Supports process-context IDs.
[49] pub process_context_ids,
/// `DCA`: Supports prefetching memory-mapped data from a device.
[50] pub memory_mapped_prefetch,
/// `SSE4_1`: Supports SSE4.1 instructions.
[51] pub sse_4_1,
/// `SSE4_2`: Supports SSE4.2 instructions.
[52] pub sse_4_2,
/// `x2APIC`: Supports the enhanced "x2" interface for the APIC.
[53] pub apic_x2,
/// Supports byte swapping with the `MOVBE` instruction.
[54] pub byte_swap_move,
/// Supports counting the set bits in a value with the `POPCNT` instruction.
[55] pub count_bits,
/// `TSC-Deadline`: Supports one-shot interrupts with the APIC using the timestamp
/// counter.
[56] pub apic_timestamp_deadline,
/// `AESNI`: Supports AES acceleration instructions.
[57] pub aes,
/// `XSAVE`: Supports instructions for saving and restoring extended processor
/// state (FPU/MMX/SSE/AVX).
[58] pub extended_state_save,
/// `OSXSAVE`: Reflects the value of [`ControlRegister4::extended_state_save`],
/// indicating that the OS has enabled the `XSAVE` feature.
[59] pub extended_state_save_enabled,
/// `AVX`: Supports AVX instructions.
[60] pub avx,
/// `F16C`: Supports conversion instructions for 16-bit floats.
[61] pub float_16_conversion,
/// Supports random number generation with the `RDRAND` instruction.
[62] pub random,
}
}
impl BasicFeatures {
/// Retrieve the feature list from the processor using the CPUID instruction.
pub fn get() -> Self {
let result = cpuid(1, 0);
// Put ECX (result[2]) in high DWord, EDX (result[3]) in low. The features in EDX
// are older and more basic, so it makes sense to start the bit numbering with
// those.
let mut features = Self(0);
features.0 |= u64::from(result[3]);
features.0 |= u64::from(result[2]) << 32;
features
}
}
bitfield! {
/// Features applicable to 64-bit processors, returned in `CPUID.80000000H:ECX+EDX`.
pub struct ExtendedFeatures(u64) {
// Bits 0..32 from EDX
/// Supports `SYSCALL`/`SYSRET` instructions. Always false if the processor is not
/// in 64-bit mode.
[11] syscall,
/// Supports no-execute (NX) bit in virtual memory pages.
[20] no_execute_bit,
/// Supports 1GB pages.
[26] page_size_1gb,
/// Supports reading the processor ID along with the timestamp counter using the
/// `RDTSCP` instruction.
[27] timestamp_with_processor,
/// Supports 64-bit mode.
[29] long_mode,
// Bits 32..64 from ECX
/// Supports the `LAHF` instruction in 64-bit mode.
[32] long_mode_ah_flags,
/// Supports counting leading zero bits with the `LZCNT` instruction.
[37] count_leading_zeros,
/// Supports hinting pending writes with the `PREFETCHW` instruction.
[40] prefetch_for_write,
}
}
impl ExtendedFeatures {
/// Retrieve the extended feature list from the processor using the CPUID instruction.
pub fn get() -> Self {
let result = cpuid(0x8000_0001, 0);
// Same ordering rationale as with Features
let mut features = Self(0);
features.0 |= u64::from(result[3]);
features.0 |= u64::from(result[2]) << 32;
features
}
}
bitfield! {
/// Indicates the processor's maximum supported physical and virtual address sizes.
pub struct AddressSpaceSizes(u32) {
/// Maximum number of bits supported in physical addresses.
[ 0.. 8] physical_address_bits: u8,
/// Maximum number of bits supported in virtual (linear) addresses.
[ 8..16] virtual_address_bits: u8,
}
}
impl AddressSpaceSizes {
/// Retrieve the supported address space sizes from the processor using the CPUID
/// instruction.
pub fn get() -> Self {
let index = 0x8000_0008;
if index <= max_cpuid_index_extended() {
Self(cpuid(index, 0)[0])
} else {
// Assume 32 bits for both
let mut result = Self::default();
result.set_physical_address_bits(32);
result.set_virtual_address_bits(32);
result
}
}
}