tartan_arch/x86_common/
features.rs

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