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 | pub fn has_cpuid() -> bool { |
102 | #[cfg (target_env = "sgx" )] |
103 | { |
104 | false |
105 | } |
106 | #[cfg (all(not(target_env = "sgx" ), target_arch = "x86_64" ))] |
107 | { |
108 | true |
109 | } |
110 | #[cfg (all(not(target_env = "sgx" ), target_arch = "x86" ))] |
111 | { |
112 | // Optimization for i586 and i686 Rust targets which SSE enabled |
113 | // and support cpuid: |
114 | #[cfg (target_feature = "sse" )] |
115 | { |
116 | true |
117 | } |
118 | |
119 | // If SSE is not enabled, detect whether cpuid is available: |
120 | #[cfg (not(target_feature = "sse" ))] |
121 | unsafe { |
122 | // On `x86` the `cpuid` instruction is not always available. |
123 | // This follows the approach indicated in: |
124 | // http://wiki.osdev.org/CPUID#Checking_CPUID_availability |
125 | // https://software.intel.com/en-us/articles/using-cpuid-to-detect-the-presence-of-sse-41-and-sse-42-instruction-sets/ |
126 | // which detects whether `cpuid` is available by checking whether |
127 | // the 21st bit of the EFLAGS register is modifiable or not. |
128 | // If it is, then `cpuid` is available. |
129 | let result: u32; |
130 | asm!( |
131 | // Read eflags and save a copy of it |
132 | "pushfd" , |
133 | "pop {result}" , |
134 | "mov {result}, {saved_flags}" , |
135 | // Flip 21st bit of the flags |
136 | "xor $0x200000, {result}" , |
137 | // Load the modified flags and read them back. |
138 | // Bit 21 can only be modified if cpuid is available. |
139 | "push {result}" , |
140 | "popfd" , |
141 | "pushfd" , |
142 | "pop {result}" , |
143 | // Use xor to find out whether bit 21 has changed |
144 | "xor {saved_flags}, {result}" , |
145 | result = out(reg) result, |
146 | saved_flags = out(reg) _, |
147 | options(nomem, att_syntax), |
148 | ); |
149 | // There is a race between popfd (A) and pushfd (B) |
150 | // where other bits beyond 21st may have been modified due to |
151 | // interrupts, a debugger stepping through the asm, etc. |
152 | // |
153 | // Therefore, explicitly check whether the 21st bit |
154 | // was modified or not. |
155 | // |
156 | // If the result is zero, the cpuid bit was not modified. |
157 | // If the result is `0x200000` (non-zero), then the cpuid |
158 | // was correctly modified and the CPU supports the cpuid |
159 | // instruction: |
160 | (result & 0x200000) != 0 |
161 | } |
162 | } |
163 | } |
164 | |
165 | /// Returns the highest-supported `leaf` (`EAX`) and sub-leaf (`ECX`) `cpuid` |
166 | /// values. |
167 | /// |
168 | /// If `cpuid` is supported, and `leaf` is zero, then the first tuple argument |
169 | /// contains the highest `leaf` value that `cpuid` supports. For `leaf`s |
170 | /// containing sub-leafs, the second tuple argument contains the |
171 | /// highest-supported sub-leaf value. |
172 | /// |
173 | /// See also [`__cpuid`](fn.__cpuid.html) and |
174 | /// [`__cpuid_count`](fn.__cpuid_count.html). |
175 | #[inline ] |
176 | #[stable (feature = "simd_x86" , since = "1.27.0" )] |
177 | pub unsafe fn __get_cpuid_max(leaf: u32) -> (u32, u32) { |
178 | let CpuidResult { eax: u32, ebx: u32, .. } = __cpuid(leaf); |
179 | (eax, ebx) |
180 | } |
181 | |
182 | #[cfg (test)] |
183 | mod tests { |
184 | use crate::core_arch::x86::*; |
185 | |
186 | #[test ] |
187 | fn test_always_has_cpuid() { |
188 | // all currently-tested targets have the instruction |
189 | // FIXME: add targets without `cpuid` to CI |
190 | assert!(cpuid::has_cpuid()); |
191 | } |
192 | |
193 | #[test ] |
194 | fn test_has_cpuid_idempotent() { |
195 | assert_eq!(cpuid::has_cpuid(), cpuid::has_cpuid()); |
196 | } |
197 | } |
198 | |