x86_64/registers/
xcontrol.rs

1//! Access to various extended system registers
2use bitflags::bitflags;
3
4/// Extended feature enable mask register
5#[derive(Debug)]
6pub struct XCr0;
7
8bitflags! {
9    /// Configuration flags of the XCr0 register.
10    ///
11    /// For MPX, [`BNDREG`](XCr0Flags::BNDREG) and [`BNDCSR`](XCr0Flags::BNDCSR) must be set/unset simultaneously.
12    /// For AVX-512, [`OPMASK`](XCr0Flags::OPMASK), [`ZMM_HI256`](XCr0Flags::ZMM_HI256), and [`HI16_ZMM`](XCr0Flags::HI16_ZMM) must be set/unset simultaneously.
13    #[repr(transparent)]
14    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
15    pub struct XCr0Flags: u64 {
16        /// Enables using the x87 FPU state
17        /// with `XSAVE`/`XRSTOR`.
18        ///
19        /// Must be set.
20        const X87 = 1;
21        /// Enables using MXCSR and the XMM registers
22        /// with `XSAVE`/`XRSTOR`.
23        ///
24        /// Must be set if [`AVX`](XCr0Flags::AVX) is set.
25        const SSE = 1 << 1;
26        /// Enables AVX instructions and using the upper halves of the AVX registers
27        /// with `XSAVE`/`XRSTOR`.
28        const AVX = 1 << 2;
29        /// Enables MPX instructions and using the BND0-BND3 bound registers
30        /// with `XSAVE`/`XRSTOR` (Intel Only).
31        const BNDREG = 1 << 3;
32        /// Enables MPX instructions and using the BNDCFGU and BNDSTATUS registers
33        /// with `XSAVE`/`XRSTOR` (Intel Only).
34        const BNDCSR = 1 << 4;
35        /// Enables AVX-512 instructions and using the K0-K7 mask registers
36        /// with `XSAVE`/`XRSTOR` (Intel Only).
37        const OPMASK = 1 << 5;
38        /// Enables AVX-512 instructions and using the upper halves of the lower ZMM registers
39        /// with `XSAVE`/`XRSTOR` (Intel Only).
40        const ZMM_HI256 = 1 << 6;
41        /// Enables AVX-512 instructions and using the upper ZMM registers
42        /// with `XSAVE`/`XRSTOR` (Intel Only).
43        const HI16_ZMM = 1 << 7;
44        /// Enables using the PKRU register
45        /// with `XSAVE`/`XRSTOR`.
46        const MPK = 1<<9;
47        /// Enables Lightweight Profiling extensions and managing LWP state
48        /// with `XSAVE`/`XRSTOR` (AMD Only).
49        const LWP = 1<<62;
50    }
51}
52
53#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
54mod x86_64 {
55    use super::*;
56    use core::arch::asm;
57
58    impl XCr0 {
59        /// Read the current set of XCR0 flags.
60        #[inline]
61        pub fn read() -> XCr0Flags {
62            XCr0Flags::from_bits_truncate(Self::read_raw())
63        }
64
65        /// Read the current raw XCR0 value.
66        #[inline]
67        pub fn read_raw() -> u64 {
68            unsafe {
69                let (low, high): (u32, u32);
70                asm!(
71                    "xgetbv",
72                    in("ecx") 0,
73                    out("rax") low, out("rdx") high,
74                    options(nomem, nostack, preserves_flags),
75                );
76                (high as u64) << 32 | (low as u64)
77            }
78        }
79
80        /// Write XCR0 flags.
81        ///
82        /// Preserves the value of reserved fields.
83        /// Panics if invalid combinations of [`XCr0Flags`] are set.
84        ///
85        /// ## Safety
86        ///
87        /// This function is unsafe because it's possible to
88        /// enable features that are not supported by the architecture
89        #[inline]
90        pub unsafe fn write(flags: XCr0Flags) {
91            let old_value = Self::read_raw();
92            let reserved = old_value & !(XCr0Flags::all().bits());
93            let new_value = reserved | flags.bits();
94
95            assert!(flags.contains(XCr0Flags::X87), "The X87 flag must be set");
96            if flags.contains(XCr0Flags::AVX) {
97                assert!(
98                    flags.contains(XCr0Flags::SSE),
99                    "AVX cannot be enabled without enabling SSE"
100                );
101            }
102            let mpx = XCr0Flags::BNDREG | XCr0Flags::BNDCSR;
103            if flags.intersects(mpx) {
104                assert!(
105                    flags.contains(mpx),
106                    "MPX flags XCr0.BNDREG and XCr0.BNDCSR must be set and unset together"
107                );
108            }
109            let avx512 = XCr0Flags::OPMASK | XCr0Flags::ZMM_HI256 | XCr0Flags::HI16_ZMM;
110            if flags.intersects(avx512) {
111                assert!(
112                    flags.contains(XCr0Flags::AVX),
113                    "AVX-512 cannot be enabled without enabling AVX"
114                );
115                assert!(
116                    flags.contains(avx512),
117                    "AVX-512 flags XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM must be set and unset together"
118                );
119            }
120
121            unsafe {
122                Self::write_raw(new_value);
123            }
124        }
125
126        /// Write raw XCR0 flags.
127        ///
128        /// Does _not_ preserve any values, including reserved fields.
129        ///
130        /// ## Safety
131        ///
132        /// This function is unsafe because it's possible to
133        /// enable features that are not supported by the architecture
134        #[inline]
135        pub unsafe fn write_raw(value: u64) {
136            let low = value as u32;
137            let high = (value >> 32) as u32;
138
139            unsafe {
140                asm!(
141                    "xsetbv",
142                    in("ecx") 0,
143                    in("rax") low, in("rdx") high,
144                    options(nomem, nostack, preserves_flags),
145                );
146            }
147        }
148    }
149}