summaryrefslogtreecommitdiffstats
path: root/library/stdarch/crates/core_arch/src/x86/cpuid.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/stdarch/crates/core_arch/src/x86/cpuid.rs')
-rw-r--r--library/stdarch/crates/core_arch/src/x86/cpuid.rs197
1 files changed, 197 insertions, 0 deletions
diff --git a/library/stdarch/crates/core_arch/src/x86/cpuid.rs b/library/stdarch/crates/core_arch/src/x86/cpuid.rs
new file mode 100644
index 000000000..6b90295ef
--- /dev/null
+++ b/library/stdarch/crates/core_arch/src/x86/cpuid.rs
@@ -0,0 +1,197 @@
+//! `cpuid` intrinsics
+#![allow(clippy::module_name_repetitions)]
+
+use crate::arch::asm;
+#[cfg(test)]
+use stdarch_test::assert_instr;
+
+/// Result of the `cpuid` instruction.
+#[allow(clippy::missing_inline_in_public_items)]
+// ^^ the derived impl of Debug for CpuidResult is not #[inline] and that's OK.
+#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+#[stable(feature = "simd_x86", since = "1.27.0")]
+pub struct CpuidResult {
+ /// EAX register.
+ #[stable(feature = "simd_x86", since = "1.27.0")]
+ pub eax: u32,
+ /// EBX register.
+ #[stable(feature = "simd_x86", since = "1.27.0")]
+ pub ebx: u32,
+ /// ECX register.
+ #[stable(feature = "simd_x86", since = "1.27.0")]
+ pub ecx: u32,
+ /// EDX register.
+ #[stable(feature = "simd_x86", since = "1.27.0")]
+ pub edx: u32,
+}
+
+/// Returns the result of the `cpuid` instruction for a given `leaf` (`EAX`)
+/// and
+/// `sub_leaf` (`ECX`).
+///
+/// The highest-supported leaf value is returned by the first tuple argument of
+/// [`__get_cpuid_max(0)`](fn.__get_cpuid_max.html). For leaves containung
+/// sub-leaves, the second tuple argument returns the highest-supported
+/// sub-leaf
+/// value.
+///
+/// The [CPUID Wikipedia page][wiki_cpuid] contains how to query which
+/// information using the `EAX` and `ECX` registers, and the interpretation of
+/// the results returned in `EAX`, `EBX`, `ECX`, and `EDX`.
+///
+/// The references are:
+/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
+/// Instruction Set Reference, A-Z][intel64_ref].
+/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
+/// System Instructions][amd64_ref].
+///
+/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
+/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
+/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
+#[inline]
+#[cfg_attr(test, assert_instr(cpuid))]
+#[stable(feature = "simd_x86", since = "1.27.0")]
+pub unsafe fn __cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult {
+ let eax;
+ let ebx;
+ let ecx;
+ let edx;
+
+ // LLVM sometimes reserves `ebx` for its internal use, we so we need to use
+ // a scratch register for it instead.
+ #[cfg(target_arch = "x86")]
+ {
+ asm!(
+ "movl %ebx, {0}",
+ "cpuid",
+ "xchgl %ebx, {0}",
+ lateout(reg) ebx,
+ inlateout("eax") leaf => eax,
+ inlateout("ecx") sub_leaf => ecx,
+ lateout("edx") edx,
+ options(nostack, preserves_flags, att_syntax),
+ );
+ }
+ #[cfg(target_arch = "x86_64")]
+ {
+ asm!(
+ "movq %rbx, {0:r}",
+ "cpuid",
+ "xchgq %rbx, {0:r}",
+ lateout(reg) ebx,
+ inlateout("eax") leaf => eax,
+ inlateout("ecx") sub_leaf => ecx,
+ lateout("edx") edx,
+ options(nostack, preserves_flags, att_syntax),
+ );
+ }
+ CpuidResult { eax, ebx, ecx, edx }
+}
+
+/// See [`__cpuid_count`](fn.__cpuid_count.html).
+#[inline]
+#[cfg_attr(test, assert_instr(cpuid))]
+#[stable(feature = "simd_x86", since = "1.27.0")]
+pub unsafe fn __cpuid(leaf: u32) -> CpuidResult {
+ __cpuid_count(leaf, 0)
+}
+
+/// Does the host support the `cpuid` instruction?
+#[inline]
+pub fn has_cpuid() -> bool {
+ #[cfg(target_env = "sgx")]
+ {
+ false
+ }
+ #[cfg(all(not(target_env = "sgx"), target_arch = "x86_64"))]
+ {
+ true
+ }
+ #[cfg(all(not(target_env = "sgx"), target_arch = "x86"))]
+ {
+ // Optimization for i586 and i686 Rust targets which SSE enabled
+ // and support cpuid:
+ #[cfg(target_feature = "sse")]
+ {
+ true
+ }
+
+ // If SSE is not enabled, detect whether cpuid is available:
+ #[cfg(not(target_feature = "sse"))]
+ unsafe {
+ // On `x86` the `cpuid` instruction is not always available.
+ // This follows the approach indicated in:
+ // http://wiki.osdev.org/CPUID#Checking_CPUID_availability
+ // https://software.intel.com/en-us/articles/using-cpuid-to-detect-the-presence-of-sse-41-and-sse-42-instruction-sets/
+ // which detects whether `cpuid` is available by checking whether
+ // the 21st bit of the EFLAGS register is modifiable or not.
+ // If it is, then `cpuid` is available.
+ let result: u32;
+ asm!(
+ // Read eflags and save a copy of it
+ "pushfd",
+ "pop {result}",
+ "mov {result}, {saved_flags}",
+ // Flip 21st bit of the flags
+ "xor $0x200000, {result}",
+ // Load the modified flags and read them back.
+ // Bit 21 can only be modified if cpuid is available.
+ "push {result}",
+ "popfd",
+ "pushfd",
+ "pop {result}",
+ // Use xor to find out whether bit 21 has changed
+ "xor {saved_flags}, {result}",
+ result = out(reg) result,
+ saved_flags = out(reg) _,
+ options(nomem, att_syntax),
+ );
+ // There is a race between popfd (A) and pushfd (B)
+ // where other bits beyond 21st may have been modified due to
+ // interrupts, a debugger stepping through the asm, etc.
+ //
+ // Therefore, explicitly check whether the 21st bit
+ // was modified or not.
+ //
+ // If the result is zero, the cpuid bit was not modified.
+ // If the result is `0x200000` (non-zero), then the cpuid
+ // was correctly modified and the CPU supports the cpuid
+ // instruction:
+ (result & 0x200000) != 0
+ }
+ }
+}
+
+/// Returns the highest-supported `leaf` (`EAX`) and sub-leaf (`ECX`) `cpuid`
+/// values.
+///
+/// If `cpuid` is supported, and `leaf` is zero, then the first tuple argument
+/// contains the highest `leaf` value that `cpuid` supports. For `leaf`s
+/// containing sub-leafs, the second tuple argument contains the
+/// highest-supported sub-leaf value.
+///
+/// See also [`__cpuid`](fn.__cpuid.html) and
+/// [`__cpuid_count`](fn.__cpuid_count.html).
+#[inline]
+#[stable(feature = "simd_x86", since = "1.27.0")]
+pub unsafe fn __get_cpuid_max(leaf: u32) -> (u32, u32) {
+ let CpuidResult { eax, ebx, .. } = __cpuid(leaf);
+ (eax, ebx)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::core_arch::x86::*;
+
+ #[test]
+ fn test_always_has_cpuid() {
+ // all currently-tested targets have the instruction
+ // FIXME: add targets without `cpuid` to CI
+ assert!(cpuid::has_cpuid());
+ }
+
+ #[test]
+ fn test_has_cpuid_idempotent() {
+ assert_eq!(cpuid::has_cpuid(), cpuid::has_cpuid());
+ }
+}