1//! `cpuid` intrinsics
2#![allow(clippy::module_name_repetitions)]
3
4use crate::arch::asm;
5#[cfg(test)]
6use stdarch_test::assert_instr;
7
8/// Result of the `cpuid` instruction.
9#[allow(clippy::missing_inline_in_public_items)]
10// ^^ the derived impl of Debug for CpuidResult is not #[inline] and that's OK.
11#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
12#[stable(feature = "simd_x86", since = "1.27.0")]
13pub struct CpuidResult {
14 /// EAX register.
15 #[stable(feature = "simd_x86", since = "1.27.0")]
16 pub eax: u32,
17 /// EBX register.
18 #[stable(feature = "simd_x86", since = "1.27.0")]
19 pub ebx: u32,
20 /// ECX register.
21 #[stable(feature = "simd_x86", since = "1.27.0")]
22 pub ecx: u32,
23 /// EDX register.
24 #[stable(feature = "simd_x86", since = "1.27.0")]
25 pub edx: u32,
26}
27
28/// Returns the result of the `cpuid` instruction for a given `leaf` (`EAX`)
29/// and
30/// `sub_leaf` (`ECX`).
31///
32/// The highest-supported leaf value is returned by the first tuple argument of
33/// [`__get_cpuid_max(0)`](fn.__get_cpuid_max.html). For leaves containung
34/// sub-leaves, the second tuple argument returns the highest-supported
35/// sub-leaf
36/// value.
37///
38/// The [CPUID Wikipedia page][wiki_cpuid] contains how to query which
39/// information using the `EAX` and `ECX` registers, and the interpretation of
40/// the results returned in `EAX`, `EBX`, `ECX`, and `EDX`.
41///
42/// The references are:
43/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
44/// Instruction Set Reference, A-Z][intel64_ref].
45/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
46/// System Instructions][amd64_ref].
47///
48/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
49/// [intel64_ref]: https://cdrdv2-public.intel.com/671110/325383-sdm-vol-2abcd.pdf
50/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
51#[inline]
52#[cfg_attr(test, assert_instr(cpuid))]
53#[stable(feature = "simd_x86", since = "1.27.0")]
54pub unsafe fn __cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult {
55 let eax;
56 let ebx;
57 let ecx;
58 let edx;
59
60 // LLVM sometimes reserves `ebx` for its internal use, we so we need to use
61 // a scratch register for it instead.
62 #[cfg(target_arch = "x86")]
63 {
64 asm!(
65 "mov {0}, ebx",
66 "cpuid",
67 "xchg {0}, ebx",
68 out(reg) ebx,
69 inout("eax") leaf => eax,
70 inout("ecx") sub_leaf => ecx,
71 out("edx") edx,
72 options(nostack, preserves_flags),
73 );
74 }
75 #[cfg(target_arch = "x86_64")]
76 {
77 asm!(
78 "mov {0:r}, rbx",
79 "cpuid",
80 "xchg {0:r}, rbx",
81 out(reg) ebx,
82 inout("eax") leaf => eax,
83 inout("ecx") sub_leaf => ecx,
84 out("edx") edx,
85 options(nostack, preserves_flags),
86 );
87 }
88 CpuidResult { eax, ebx, ecx, edx }
89}
90
91/// See [`__cpuid_count`](fn.__cpuid_count.html).
92#[inline]
93#[cfg_attr(test, assert_instr(cpuid))]
94#[stable(feature = "simd_x86", since = "1.27.0")]
95pub unsafe fn __cpuid(leaf: u32) -> CpuidResult {
96 __cpuid_count(leaf, sub_leaf:0)
97}
98
99/// Does the host support the `cpuid` instruction?
100#[inline]
101#[unstable(feature = "stdarch_x86_has_cpuid", issue = "60123")]
102pub fn has_cpuid() -> bool {
103 #[cfg(target_env = "sgx")]
104 {
105 false
106 }
107 #[cfg(all(not(target_env = "sgx"), target_arch = "x86_64"))]
108 {
109 true
110 }
111 #[cfg(all(not(target_env = "sgx"), target_arch = "x86"))]
112 {
113 // Optimization for i586 and i686 Rust targets which SSE enabled
114 // and support cpuid:
115 #[cfg(target_feature = "sse")]
116 {
117 true
118 }
119
120 // If SSE is not enabled, detect whether cpuid is available:
121 #[cfg(not(target_feature = "sse"))]
122 unsafe {
123 // On `x86` the `cpuid` instruction is not always available.
124 // This follows the approach indicated in:
125 // http://wiki.osdev.org/CPUID#Checking_CPUID_availability
126 // https://software.intel.com/en-us/articles/using-cpuid-to-detect-the-presence-of-sse-41-and-sse-42-instruction-sets/
127 // which detects whether `cpuid` is available by checking whether
128 // the 21st bit of the EFLAGS register is modifiable or not.
129 // If it is, then `cpuid` is available.
130 let result: u32;
131 asm!(
132 // Read eflags and save a copy of it
133 "pushfd",
134 "pop {result}",
135 "mov {result}, {saved_flags}",
136 // Flip 21st bit of the flags
137 "xor $0x200000, {result}",
138 // Load the modified flags and read them back.
139 // Bit 21 can only be modified if cpuid is available.
140 "push {result}",
141 "popfd",
142 "pushfd",
143 "pop {result}",
144 // Use xor to find out whether bit 21 has changed
145 "xor {saved_flags}, {result}",
146 result = out(reg) result,
147 saved_flags = out(reg) _,
148 options(nomem, att_syntax),
149 );
150 // There is a race between popfd (A) and pushfd (B)
151 // where other bits beyond 21st may have been modified due to
152 // interrupts, a debugger stepping through the asm, etc.
153 //
154 // Therefore, explicitly check whether the 21st bit
155 // was modified or not.
156 //
157 // If the result is zero, the cpuid bit was not modified.
158 // If the result is `0x200000` (non-zero), then the cpuid
159 // was correctly modified and the CPU supports the cpuid
160 // instruction:
161 (result & 0x200000) != 0
162 }
163 }
164}
165
166/// Returns the highest-supported `leaf` (`EAX`) and sub-leaf (`ECX`) `cpuid`
167/// values.
168///
169/// If `cpuid` is supported, and `leaf` is zero, then the first tuple argument
170/// contains the highest `leaf` value that `cpuid` supports. For `leaf`s
171/// containing sub-leafs, the second tuple argument contains the
172/// highest-supported sub-leaf value.
173///
174/// See also [`__cpuid`](fn.__cpuid.html) and
175/// [`__cpuid_count`](fn.__cpuid_count.html).
176#[inline]
177#[stable(feature = "simd_x86", since = "1.27.0")]
178pub unsafe fn __get_cpuid_max(leaf: u32) -> (u32, u32) {
179 let CpuidResult { eax: u32, ebx: u32, .. } = __cpuid(leaf);
180 (eax, ebx)
181}
182
183#[cfg(test)]
184mod tests {
185 use crate::core_arch::x86::*;
186
187 #[test]
188 #[cfg_attr(miri, ignore)] // Uses inline assembly
189 fn test_always_has_cpuid() {
190 // all currently-tested targets have the instruction
191 // FIXME: add targets without `cpuid` to CI
192 assert!(cpuid::has_cpuid());
193 }
194
195 #[test]
196 #[cfg_attr(miri, ignore)] // Uses inline assembly
197 fn test_has_cpuid_idempotent() {
198 assert_eq!(cpuid::has_cpuid(), cpuid::has_cpuid());
199 }
200}
201