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.
32///
33/// # Safety
34///
35/// `cpuid` instruction must be available. See [`cpuid`].
36pub unsafe fn supports_rdrand() -> bool {
37    unsafe { cpuid(RDRAND_LEAF, RDRAND_SUB_LEAF) }.ecx & RDRAND_FLAG != 0
38}
39
40/// Whether the current CPU supports the `rdseed` instruction.
41///
42/// # Safety
43///
44/// `cpuid` instruction must be available. See [`cpuid`].
45pub unsafe fn supports_rdseed() -> bool {
46    unsafe { cpuid(RDSEED_LEAF, RDSEED_SUB_LEAF) }.ebx & RDSEED_FLAG != 0
47}
48
49/// Execute the cpuid instruction for the given leaf and sub_leaf.
50///
51/// For leaves that don't have sub-leaves, providing a sub-leaf shouldn't change
52/// the result. The reverse is of course not true; failing to provide a sub-leaf
53/// will result in returning an arbitrary one.
54///
55/// # Safety
56///
57/// `cpuid` instruction must be available. This is generally true outside of
58/// specialized execution environments such as SGX. See
59/// <https://github.com/rust-lang/rust/issues/60123>.
60pub unsafe fn cpuid(leaf: u32, sub_leaf: Option<u32>) -> CpuidResult {
61    match sub_leaf {
62        // SAFETY: Caller's responsibility.
63        Some(sub_leaf) => unsafe { __cpuid_count(leaf, sub_leaf) },
64        None => unsafe { __cpuid(leaf) },
65    }
66}
67
68/// Whether `ip` points to a cpuid instruction.
69///
70/// # Safety
71///
72/// `ip` must be a dereferenceable pointer, pointing to the
73/// beginning of a valid x86_64 instruction.
74pub unsafe fn ip_is_cpuid(ip: *const u8) -> bool {
75    unsafe { ip_matches(ip, &CPUID) }
76}
77
78mod export {
79    /// Whether `buf` begins with a cpuid instruction.
80    ///
81    /// # Safety
82    ///
83    /// `ip` must be a dereferenceable pointer, pointing to the
84    /// beginning of a valid x86_64 instruction.
85    #[unsafe(no_mangle)]
86    pub unsafe extern "C-unwind" fn isCpuid(ip: *const u8) -> bool {
87        unsafe { super::ip_is_cpuid(ip) }
88    }
89}
90
91#[cfg(test)]
92mod test {
93    use super::*;
94
95    // miri doesn't support inline asm.
96    #[cfg(not(miri))]
97    #[test]
98    fn test_supports_rdrand() {
99        let inlined_res = unsafe { __cpuid(1) }.ecx & (1 << 30) != 0;
100        assert_eq!(unsafe { supports_rdrand() }, inlined_res);
101    }
102
103    // miri doesn't support inline asm.
104    #[cfg(not(miri))]
105    #[test]
106    fn test_supports_rdseed() {
107        let inlined_res = unsafe { __cpuid_count(7, 0) }.ebx & (1 << 18) != 0;
108        assert_eq!(unsafe { supports_rdseed() }, inlined_res);
109    }
110}