forked from rust-lang/stdarch
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpuid.rs
More file actions
120 lines (105 loc) · 3.44 KB
/
Copy pathcpuid.rs
File metadata and controls
120 lines (105 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! `cpuid` intrinsics
#[cfg(test)]
use stdsimd_test::assert_instr;
/// Result of the `cpuid` instruction.
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
pub struct CpuidResult {
/// EAX register.
pub eax: u32,
/// EBX register.
pub ebx: u32,
/// ECX register.
pub ecx: u32,
/// EDX register.
pub edx: u32,
}
/// `cpuid` instruction.
///
/// The [CPUID Wikipedia page][wiki_cpuid] contains how to query which
/// information using the `eax` and `ecx` registers, and the format in
/// which this information is returned in `eax...edx`.
///
/// The `has_cpuid()` intrinsics can be used to query whether the `cpuid`
/// instruction is available.
///
/// The definitive 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(always)]
#[cfg_attr(test, assert_instr(cpuid))]
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
pub unsafe fn __cpuid_count(eax: u32, ecx: u32) -> CpuidResult {
let mut r = ::std::mem::uninitialized::<CpuidResult>();
asm!("cpuid"
: "={eax}"(r.eax), "={ebx}"(r.ebx), "={ecx}"(r.ecx), "={edx}"(r.edx)
: "{eax}"(eax), "{ecx}"(ecx)
: :);
r
}
/// `cpuid` instruction.
///
/// See `__cpuid_count`.
#[inline(always)]
#[cfg_attr(test, assert_instr(cpuid))]
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
pub unsafe fn __cpuid(eax: u32) -> CpuidResult {
__cpuid_count(eax, 0)
}
/// Does the host support the `cpuid` instruction?
#[inline(always)]
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
pub fn has_cpuid() -> bool {
#[cfg(target_arch = "x86_64")]
{
true
}
#[cfg(target_arch = "x86")]
{
use super::ia32::{__readeflags, __writeeflags};
// On `x86` the `cpuid` instruction is not always available.
// This follows the approach indicated in:
// http://wiki.osdev.org/CPUID#Checking_CPUID_availability
unsafe {
// Read EFLAGS:
let eflags: u32 = __readeflags();
// Invert the ID bit in EFLAGS:
let eflags_mod: u32 = eflags | 0x0020_0000;
// Store the modified EFLAGS (ID bit may or may not be inverted)
__writeeflags(eflags_mod);
// Read EFLAGS again:
let eflags_after: u32 = __readeflags();
// Check if the ID bit changed:
eflags_after != eflags
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_always_has_cpuid() {
// all currently-tested targets have the instruction
// FIXME: add targets without `cpuid` to CI
assert!(has_cpuid());
}
#[cfg(target_arch = "x86")]
#[test]
fn test_has_cpuid() {
use vendor::__readeflags;
unsafe {
let before = __readeflags();
if has_cpuid() {
assert!(before != __readeflags());
} else {
assert!(before == __readeflags());
}
}
}
}