1 | //! `cpuid` intrinsics |
2 | #![allow (clippy::module_name_repetitions)] |
3 | |
4 | use crate::arch::asm; |
5 | #[cfg (test)] |
6 | use 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" )] |
13 | pub 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" )] |
54 | pub 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" )] |
95 | pub 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" )] |
102 | pub 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" )] |
178 | pub 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)] |
184 | mod 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 | |