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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
//! 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.
///
/// For MPX, [`BNDREG`](XCr0Flags::BNDREG) and [`BNDCSR`](XCr0Flags::BNDCSR) must be set/unset simultaneously.
/// For AVX-512, [`OPMASK`](XCr0Flags::OPMASK), [`ZMM_HI256`](XCr0Flags::ZMM_HI256), and [`HI16_ZMM`](XCr0Flags::HI16_ZMM) must be set/unset simultaneously.
pub struct XCr0Flags: u64 {
/// Enables using the x87 FPU state
/// with `XSAVE`/`XRSTOR`.
///
/// Must be set.
const X87 = 1;
/// Enables using MXCSR and the XMM registers
/// with `XSAVE`/`XRSTOR`.
///
/// Must be set if [`AVX`](XCr0Flags::AVX) is set.
const SSE = 1 << 1;
/// Enables AVX instructions and using the upper halves of the AVX registers
/// with `XSAVE`/`XRSTOR`.
const AVX = 1 << 2;
/// Alias for [`AVX`](XCr0Flags::AVX)
#[deprecated(since = "0.14.5", note = "use `AVX` instead")]
const YMM = 1<<2;
/// Enables MPX instructions and using the BND0-BND3 bound registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const BNDREG = 1 << 3;
/// Enables MPX instructions and using the BNDCFGU and BNDSTATUS registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const BNDCSR = 1 << 4;
/// Enables AVX-512 instructions and using the K0-K7 mask registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const OPMASK = 1 << 5;
/// Enables AVX-512 instructions and using the upper halves of the lower ZMM registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const ZMM_HI256 = 1 << 6;
/// Enables AVX-512 instructions and using the upper ZMM registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const HI16_ZMM = 1 << 7;
/// Enables using the PKRU register
/// with `XSAVE`/`XRSTOR`.
const MPK = 1<<9;
/// Enables Lightweight Profiling extensions and managing LWP state
/// with `XSAVE`/`XRSTOR` (AMD Only).
const LWP = 1<<62;
}
}
#[cfg(feature = "instructions")]
mod x86_64 {
use super::*;
use core::arch::asm;
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 {
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)
}
}
/// Write XCR0 flags.
///
/// Preserves the value of reserved fields.
/// Panics if invalid combinations of [`XCr0Flags`] are set.
///
/// ## 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();
assert!(flags.contains(XCr0Flags::X87), "The X87 flag must be set");
if flags.contains(XCr0Flags::AVX) {
assert!(
flags.contains(XCr0Flags::SSE),
"AVX cannot be enabled without enabling SSE"
);
}
let mpx = XCr0Flags::BNDREG | XCr0Flags::BNDCSR;
if flags.intersects(mpx) {
assert!(
flags.contains(mpx),
"MPX flags XCr0.BNDREG and XCr0.BNDCSR must be set and unset together"
);
}
let avx512 = XCr0Flags::OPMASK | XCr0Flags::ZMM_HI256 | XCr0Flags::HI16_ZMM;
if flags.intersects(avx512) {
assert!(
flags.contains(XCr0Flags::AVX),
"AVX-512 cannot be enabled without enabling AVX"
);
assert!(
flags.contains(avx512),
"AVX-512 flags XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM must be set and unset together"
);
}
unsafe {
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;
unsafe {
asm!(
"xsetbv",
in("ecx") 0,
in("rax") low, in("rdx") high,
options(nomem, nostack, preserves_flags),
);
}
}
}
}