From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- library/stdarch/crates/core_arch/src/x86/cpuid.rs | 197 ++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 library/stdarch/crates/core_arch/src/x86/cpuid.rs (limited to 'library/stdarch/crates/core_arch/src/x86/cpuid.rs') 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()); + } +} -- cgit v1.2.3