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}