Skip to main content

asm_util/
cpuid.rs

1use core::arch::x86_64::{__cpuid, __cpuid_count};
2
3use crate::ip_matches;
4
5/* Constants below primarily from
6 * <https://en.wikipedia.org/wiki/CPUID>. TODO: cross-check/cite primary
7 * sources.
8 */
9
10/// Length in bytes of an x86-64 cpuid instruction.
11pub const CPUID_INSN_LEN: usize = 2;
12/// An x86-64 cpuid instruction.
13pub const CPUID: [u8; CPUID_INSN_LEN] = [0x0f, 0xa2];
14
15/// cpuid leaf for finding the rdrand bit.
16pub const RDRAND_LEAF: u32 = 1;
17/// cpuid leaf 1 doesn't have sub-leaves.
18pub const RDRAND_SUB_LEAF: Option<u32> = None;
19/// rdrand flag in a `cpuid(RDRAND_LEAF)` result.
20pub const RDRAND_FLAG: u32 = 1 << 30;
21
22/// cpuid leaf for finding the rdseed bit.
23pub const RDSEED_LEAF: u32 = 7;
24/// cpuid sub-leaf for finding the rdseed bit.
25pub const RDSEED_SUB_LEAF: Option<u32> = Some(0);
26/// rdseed flag in a `cpuid_count(RDSEED_LEAF, RDSEED_SUB_LEAF)` result.
27pub const RDSEED_FLAG: u32 = 1 << 18;
28
29pub use core::arch::x86_64::CpuidResult;
30
31/// Whether the current CPU supports the `rdrand` instruction.
32pub fn supports_rdrand() -> bool {
33    cpuid(RDRAND_LEAF, RDRAND_SUB_LEAF).ecx & RDRAND_FLAG != 0
34}
35
36/// Whether the current CPU supports the `rdseed` instruction.
37pub fn supports_rdseed() -> bool {
38    cpuid(RDSEED_LEAF, RDSEED_SUB_LEAF).ebx & RDSEED_FLAG != 0
39}
40
41/// Execute the cpuid instruction for the given leaf and sub_leaf.
42///
43/// For leaves that don't have sub-leaves, providing a sub-leaf shouldn't change
44/// the result. The reverse is of course not true; failing to provide a sub-leaf
45/// will result in returning an arbitrary one.
46///
47/// # Safety
48///
49/// `cpuid` instruction must be available. This is generally true outside of
50/// specialized execution environments such as SGX. See
51/// <https://github.com/rust-lang/rust/issues/60123>.
52pub fn cpuid(leaf: u32, sub_leaf: Option<u32>) -> CpuidResult {
53    match sub_leaf {
54        Some(sub_leaf) => __cpuid_count(leaf, sub_leaf),
55        None => __cpuid(leaf),
56    }
57}
58
59/// Whether `ip` points to a cpuid instruction.
60///
61/// # Safety
62///
63/// `ip` must be a dereferenceable pointer, pointing to the
64/// beginning of a valid x86_64 instruction.
65pub unsafe fn ip_is_cpuid(ip: *const u8) -> bool {
66    unsafe { ip_matches(ip, &CPUID) }
67}
68
69mod export {
70    /// Whether `buf` begins with a cpuid instruction.
71    ///
72    /// # Safety
73    ///
74    /// `ip` must be a dereferenceable pointer, pointing to the
75    /// beginning of a valid x86_64 instruction.
76    #[unsafe(no_mangle)]
77    pub unsafe extern "C-unwind" fn isCpuid(ip: *const u8) -> bool {
78        unsafe { super::ip_is_cpuid(ip) }
79    }
80}
81
82#[cfg(test)]
83mod test {
84    use super::*;
85
86    // miri doesn't support inline asm.
87    #[cfg(not(miri))]
88    #[test]
89    fn test_supports_rdrand() {
90        let inlined_res = __cpuid(1).ecx & (1 << 30) != 0;
91        assert_eq!(supports_rdrand(), inlined_res);
92    }
93
94    // miri doesn't support inline asm.
95    #[cfg(not(miri))]
96    #[test]
97    fn test_supports_rdseed() {
98        let inlined_res = __cpuid_count(7, 0).ebx & (1 << 18) != 0;
99        assert_eq!(supports_rdseed(), inlined_res);
100    }
101}