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}