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
//! Access to various extended system registers
use bitflags::bitflags;

/// Extended feature enable mask register
#[derive(Debug)]
pub struct XCr0;

bitflags! {
    /// Configuration flags of the XCr0 register.
    pub struct XCr0Flags: u64 {
        /// Enables x87 FPU
        const X87 = 1;
        /// Enables 128-bit (legacy) SSE
        /// Must be set to enable AVX and YMM
        const SSE = 1<<1;
        /// Enables 256-bit SSE
        /// Must be set to enable AVX
        const YMM = 1<<2;
        /// When set, PKRU state management is supported by
        /// ZSAVE/XRSTOR
        const MPK = 1<<9;
        /// When set the Lightweight Profiling extensions are enabled
        const LWP = 1<<62;
    }
}

#[cfg(feature = "instructions")]
mod x86_64 {
    use super::*;
    impl XCr0 {
        /// Read the current set of XCR0 flags.
        #[inline]
        pub fn read() -> XCr0Flags {
            XCr0Flags::from_bits_truncate(Self::read_raw())
        }

        /// Read the current raw XCR0 value.
        #[inline]
        pub fn read_raw() -> u64 {
            #[cfg(feature = "inline_asm")]
            unsafe {
                let (low, high): (u32, u32);
                asm!(
                    "xgetbv",
                    in("ecx") 0,
                    out("rax") low, out("rdx") high,
                    options(nomem, nostack, preserves_flags),
                );
                (high as u64) << 32 | (low as u64)
            }

            #[cfg(not(feature = "inline_asm"))]
            unsafe {
                crate::asm::x86_64_asm_xgetbv(0)
            }
        }

        /// Write XCR0 flags.
        ///
        /// Preserves the value of reserved fields.
        ///
        /// ## Safety
        ///
        /// This function is unsafe because it's possible to
        /// enable features that are not supported by the architecture
        #[inline]
        pub unsafe fn write(flags: XCr0Flags) {
            let old_value = Self::read_raw();
            let reserved = old_value & !(XCr0Flags::all().bits());
            let new_value = reserved | flags.bits();

            Self::write_raw(new_value);
        }

        /// Write raw XCR0 flags.
        ///
        /// Does _not_ preserve any values, including reserved fields.
        ///
        /// ## Safety
        ///
        /// This function is unsafe because it's possible to
        /// enable features that are not supported by the architecture
        #[inline]
        pub unsafe fn write_raw(value: u64) {
            let low = value as u32;
            let high = (value >> 32) as u32;

            #[cfg(feature = "inline_asm")]
            asm!(
                "xsetbv",
                in("ecx") 0,
                in("rax") low, in("rdx") high,
                options(nomem, nostack, preserves_flags),
            );

            #[cfg(not(feature = "inline_asm"))]
            crate::asm::x86_64_asm_xsetbv(0, low, high);
        }
    }
}