diff --git a/src/asm/asm.s b/src/asm/asm.s index 16f11c6c1..d679108bf 100644 --- a/src/asm/asm.s +++ b/src/asm/asm.s @@ -173,6 +173,36 @@ _x86_64_asm_load_gs: mov %di, %gs retq +.global _x86_64_asm_get_ss +.p2align 4 +_x86_64_asm_get_ss: + mov %ss, %ax + retq + +.global _x86_64_asm_get_ds +.p2align 4 +_x86_64_asm_get_ds: + mov %ds, %ax + retq + +.global _x86_64_asm_get_es +.p2align 4 +_x86_64_asm_get_es: + mov %es, %ax + retq + +.global _x86_64_asm_get_fs +.p2align 4 +_x86_64_asm_get_fs: + mov %fs, %ax + retq + +.global _x86_64_asm_get_gs +.p2align 4 +_x86_64_asm_get_gs: + mov %gs, %ax + retq + .global _x86_64_asm_swapgs .p2align 4 _x86_64_asm_swapgs: diff --git a/src/asm/mod.rs b/src/asm/mod.rs index 57ec996b8..9b9ddbdea 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -1,5 +1,5 @@ #[link(name = "x86_64_asm", kind = "static")] -extern "C" { +extern "sysv64" { #[cfg_attr( any(target_env = "gnu", target_env = "musl"), link_name = "_x86_64_asm_interrupt_enable" @@ -114,6 +114,36 @@ extern "C" { )] pub(crate) fn x86_64_asm_load_gs(sel: u16); + #[cfg_attr( + any(target_env = "gnu", target_env = "musl"), + link_name = "_x86_64_asm_get_ss" + )] + pub(crate) fn x86_64_asm_get_ss() -> u16; + + #[cfg_attr( + any(target_env = "gnu", target_env = "musl"), + link_name = "_x86_64_asm_get_ds" + )] + pub(crate) fn x86_64_asm_get_ds() -> u16; + + #[cfg_attr( + any(target_env = "gnu", target_env = "musl"), + link_name = "_x86_64_asm_get_es" + )] + pub(crate) fn x86_64_asm_get_es() -> u16; + + #[cfg_attr( + any(target_env = "gnu", target_env = "musl"), + link_name = "_x86_64_asm_get_fs" + )] + pub(crate) fn x86_64_asm_get_fs() -> u16; + + #[cfg_attr( + any(target_env = "gnu", target_env = "musl"), + link_name = "_x86_64_asm_get_gs" + )] + pub(crate) fn x86_64_asm_get_gs() -> u16; + #[cfg_attr( any(target_env = "gnu", target_env = "musl"), link_name = "_x86_64_asm_swapgs" diff --git a/src/instructions/segmentation.rs b/src/instructions/segmentation.rs index 7de0fbb59..2ca05ac83 100644 --- a/src/instructions/segmentation.rs +++ b/src/instructions/segmentation.rs @@ -1,210 +1,305 @@ //! Provides functions to read and write segment registers. -use crate::structures::gdt::SegmentSelector; +#[cfg(docsrs)] +use crate::{ + registers::control::Cr4Flags, + structures::gdt::{Descriptor, GlobalDescriptorTable}, +}; +use crate::{ + registers::model_specific::{FsBase, GsBase, Msr}, + structures::gdt::SegmentSelector, + VirtAddr, +}; -/// Reload code segment register. +/// An x86 segment /// -/// Note this is special since we can not directly move -/// to cs. Instead we push the new segment selector -/// and return value on the stack and use retf -/// to reload cs and continue at 1:. -/// -/// ## Safety -/// -/// This function is unsafe because the caller must ensure that `sel` -/// is a valid code segment descriptor. -#[inline] -pub unsafe fn set_cs(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] - asm!( - "push {sel}", - "lea {tmp}, [1f + rip]", - "push {tmp}", - "retfq", - "1:", - sel = in(reg) u64::from(sel.0), - tmp = lateout(reg) _, - options(preserves_flags), - ); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_set_cs(u64::from(sel.0)); -} - -/// Reload stack segment register. -/// -/// ## Safety +/// Segment registers on x86 are 16-bit [`SegmentSelector`]s, which index into +/// the [`GlobalDescriptorTable`]. The corresponding GDT entry is used to +/// configure the segment itself. Note that most segmentation functionality is +/// disabled in 64-bit mode. See the individual segments for more information. +pub trait Segment { + /// Returns the current value of the segment register. + fn get_reg() -> SegmentSelector; + /// Reload the segment register. Depending on the segment, this may also + /// reconfigure the corresponding segment. + /// + /// ## Safety + /// + /// This function is unsafe because the caller must ensure that `sel` + /// is a valid segment descriptor, and that reconfiguring the segment will + /// not cause undefined behavior. + unsafe fn set_reg(sel: SegmentSelector); +} + +/// An x86 segment which is actually used in 64-bit mode /// -/// This function is unsafe because the caller must ensure that `sel` -/// is a valid stack segment descriptor. -#[inline] -pub unsafe fn load_ss(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] - asm!("mov ss, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); +/// While most segments are unused in 64-bit mode, the FS and GS segments are +/// still partially used. Only the 64-bit segment base address is used, and this +/// address can be set via the GDT, or by using the `FSGSBASE` instructions. +pub trait Segment64: Segment { + /// MSR containing the segment base. This MSR can be used to set the base + /// when [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is **not** set. + const BASE: Msr; + /// Reads the segment base address + /// + /// ## Exceptions + /// + /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`. + fn read_base() -> VirtAddr; + /// Writes the segment base address + /// + /// ## Exceptions + /// + /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`. + /// + /// ## Safety + /// + /// The caller must ensure that this write operation has no unsafe side + /// effects, as the segment base address might be in use. + unsafe fn write_base(base: VirtAddr); +} - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_load_ss(sel.0); +macro_rules! get_reg_impl { + ($name:literal, $asm_get:ident) => { + fn get_reg() -> SegmentSelector { + let segment: u16; + #[cfg(feature = "inline_asm")] + unsafe { + asm!(concat!("mov {0:x}, ", $name), out(reg) segment, options(nomem, nostack, preserves_flags)); + } + #[cfg(not(feature = "inline_asm"))] + unsafe { + segment = crate::asm::$asm_get(); + } + SegmentSelector(segment) + } + }; } -/// Reload data segment register. -/// -/// ## Safety -/// -/// This function is unsafe because the caller must ensure that `sel` -/// is a valid data segment descriptor. -#[inline] -pub unsafe fn load_ds(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] - asm!("mov ds, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); +macro_rules! segment_impl { + ($type:ty, $name:literal, $asm_get:ident, $asm_load:ident) => { + impl Segment for $type { + get_reg_impl!($name, $asm_get); + + unsafe fn set_reg(sel: SegmentSelector) { + #[cfg(feature = "inline_asm")] + asm!(concat!("mov ", $name, ", {0:x}"), in(reg) sel.0, options(nostack, preserves_flags)); - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_load_ds(sel.0); + #[cfg(not(feature = "inline_asm"))] + crate::asm::$asm_load(sel.0); + } + } + }; } -/// Reload es segment register. -/// -/// ## Safety -/// -/// This function is unsafe because the caller must ensure that `sel` -/// is a valid extra segment descriptor. -#[inline] -pub unsafe fn load_es(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] - asm!("mov es, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); +macro_rules! segment64_impl { + ($type:ty, $name:literal, $base:ty, $asm_rd:ident, $asm_wr:ident) => { + impl Segment64 for $type { + const BASE: Msr = <$base>::MSR; + fn read_base() -> VirtAddr { + #[cfg(feature = "inline_asm")] + unsafe { + let val: u64; + asm!(concat!("rd", $name, "base {}"), out(reg) val, options(nomem, nostack, preserves_flags)); + VirtAddr::new_unsafe(val) + } + #[cfg(not(feature = "inline_asm"))] + unsafe { + VirtAddr::new_unsafe(crate::asm::$asm_rd()) + } + } + + unsafe fn write_base(base: VirtAddr) { + #[cfg(feature = "inline_asm")] + asm!(concat!("wr", $name, "base {}"), in(reg) base.as_u64(), options(nostack, preserves_flags)); - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_load_es(sel.0); + #[cfg(not(feature = "inline_asm"))] + crate::asm::$asm_wr(base.as_u64()); + } + } + }; } -/// Reload fs segment register. +/// Code Segment /// -/// ## Safety -/// -/// This function is unsafe because the caller must ensure that `sel` -/// is a valid fs segment descriptor. -#[inline] -pub unsafe fn load_fs(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] - asm!("mov fs, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); +/// The segment base and limit are unused in 64-bit mode. Only the L (long), D +/// (default operation size), and DPL (descriptor privilege-level) fields of the +/// descriptor are recognized. So changing the segment register can be used to +/// change privilege level or enable/disable long mode. +#[derive(Debug)] +pub struct CS; +impl Segment for CS { + get_reg_impl!("cs", x86_64_asm_get_cs); - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_load_fs(sel.0); + /// Note this is special since we cannot directly move to [`CS`]. Instead we + /// push the new segment selector and return value on the stack and use + /// `retfq` to reload [`CS`] and continue at the end of our function. + unsafe fn set_reg(sel: SegmentSelector) { + #[cfg(feature = "inline_asm")] + asm!( + "push {sel}", + "lea {tmp}, [1f + rip]", + "push {tmp}", + "retfq", + "1:", + sel = in(reg) u64::from(sel.0), + tmp = lateout(reg) _, + options(preserves_flags), + ); + + #[cfg(not(feature = "inline_asm"))] + crate::asm::x86_64_asm_set_cs(u64::from(sel.0)); + } } -/// Reload gs segment register. +/// Stack Segment /// -/// ## Safety +/// Entirely unused in 64-bit mode; setting the segment register does nothing. +/// However, in ring 3, the SS register still has to point to a valid +/// [`Descriptor`] (it cannot be zero). This means a user-mode read/write +/// segment descriptor must be present in the GDT. /// -/// This function is unsafe because the caller must ensure that `sel` -/// is a valid gs segment descriptor. -#[inline] -pub unsafe fn load_gs(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] - asm!("mov gs, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); +/// This register is also set by the `syscall`/`sysret` and +/// `sysenter`/`sysexit` instructions (even on 64-bit transitions). This is to +/// maintain symmetry with 32-bit transitions where setting SS actually will +/// actually have an effect. +#[derive(Debug)] +pub struct SS; +segment_impl!(SS, "ss", x86_64_asm_get_ss, x86_64_asm_load_ss); - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_load_gs(sel.0); -} +/// Data Segment +/// +/// Entirely unused in 64-bit mode; setting the segment register does nothing. +#[derive(Debug)] +pub struct DS; +segment_impl!(DS, "ds", x86_64_asm_get_ds, x86_64_asm_load_ds); -/// Swap `KernelGsBase` MSR and `GsBase` MSR. +/// ES Segment /// -/// ## Safety +/// Entirely unused in 64-bit mode; setting the segment register does nothing. +#[derive(Debug)] +pub struct ES; +segment_impl!(ES, "es", x86_64_asm_get_es, x86_64_asm_load_es); + +/// FS Segment /// -/// This function is unsafe because the caller must ensure that the -/// swap operation cannot lead to undefined behavior. -#[inline] -pub unsafe fn swap_gs() { - #[cfg(feature = "inline_asm")] - asm!("swapgs", options(nostack, preserves_flags)); +/// Only base is used in 64-bit mode, see [`Segment64`]. This is often used in +/// user-mode for Thread-Local Storage (TLS). +#[derive(Debug)] +pub struct FS; +segment_impl!(FS, "fs", x86_64_asm_get_fs, x86_64_asm_load_fs); +segment64_impl!(FS, "fs", FsBase, x86_64_asm_rdfsbase, x86_64_asm_wrfsbase); - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_swapgs(); -} +/// GS Segment +/// +/// Only base is used in 64-bit mode, see [`Segment64`]. In kernel-mode, the GS +/// base often points to a per-cpu kernel data structure. +#[derive(Debug)] +pub struct GS; +segment_impl!(GS, "gs", x86_64_asm_get_gs, x86_64_asm_load_gs); +segment64_impl!(GS, "gs", GsBase, x86_64_asm_rdgsbase, x86_64_asm_wrgsbase); -/// Returns the current value of the code segment register. -#[inline] -pub fn cs() -> SegmentSelector { - let segment: u16; +impl GS { + /// Swap `KernelGsBase` MSR and `GsBase` MSR. + /// + /// ## Safety + /// + /// This function is unsafe because the caller must ensure that the + /// swap operation cannot lead to undefined behavior. + pub unsafe fn swap() { + #[cfg(feature = "inline_asm")] + asm!("swapgs", options(nostack, preserves_flags)); - #[cfg(feature = "inline_asm")] - unsafe { - asm!("mov {0:x}, cs", out(reg) segment, options(nomem, nostack, preserves_flags)); - } - #[cfg(not(feature = "inline_asm"))] - unsafe { - segment = crate::asm::x86_64_asm_get_cs(); + #[cfg(not(feature = "inline_asm"))] + crate::asm::x86_64_asm_swapgs(); } - - SegmentSelector(segment) } -/// Writes the FS segment base address -/// -/// ## Safety -/// -/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`. +/// Alias for [`CS::set_reg()`] +#[deprecated(since = "0.14.4", note = "use `CS::set_reg()` instead")] +#[allow(clippy::missing_safety_doc)] +#[inline] +pub unsafe fn set_cs(sel: SegmentSelector) { + CS::set_reg(sel) +} +/// Alias for [`SS::set_reg()`] +#[deprecated(since = "0.14.4", note = "use `SS::set_reg()` instead")] +#[allow(clippy::missing_safety_doc)] +#[inline] +pub unsafe fn load_ss(sel: SegmentSelector) { + SS::set_reg(sel) +} +/// Alias for [`DS::set_reg()`] +#[deprecated(since = "0.14.4", note = "use `DS::set_reg()` instead")] +#[allow(clippy::missing_safety_doc)] +#[inline] +pub unsafe fn load_ds(sel: SegmentSelector) { + DS::set_reg(sel) +} +/// Alias for [`ES::set_reg()`] +#[deprecated(since = "0.14.4", note = "use `ES::set_reg()` instead")] +#[allow(clippy::missing_safety_doc)] +#[inline] +pub unsafe fn load_es(sel: SegmentSelector) { + ES::set_reg(sel) +} +/// Alias for [`FS::set_reg()`] +#[deprecated(since = "0.14.4", note = "use `FS::set_reg()` instead")] +#[allow(clippy::missing_safety_doc)] +#[inline] +pub unsafe fn load_fs(sel: SegmentSelector) { + FS::set_reg(sel) +} +/// Alias for [`GS::set_reg()`] +#[deprecated(since = "0.14.4", note = "use `GS::set_reg()` instead")] +#[allow(clippy::missing_safety_doc)] +#[inline] +pub unsafe fn load_gs(sel: SegmentSelector) { + GS::set_reg(sel) +} +/// Alias for [`GS::swap()`] +#[deprecated(since = "0.14.4", note = "use `GS::swap()` instead")] +#[allow(clippy::missing_safety_doc)] +#[inline] +pub unsafe fn swap_gs() { + GS::swap() +} +/// Alias for [`CS::get_reg()`] +#[deprecated(since = "0.14.4", note = "use `CS::get_reg()` instead")] +#[allow(clippy::missing_safety_doc)] +#[inline] +pub fn cs() -> SegmentSelector { + CS::get_reg() +} +/// Alias for [`FS::write_base()`]. /// -/// The caller must ensure that this write operation has no unsafe side -/// effects, as the FS segment base address is often used for thread -/// local storage. +/// Panics if the provided address is non-canonical. +#[deprecated(since = "0.14.4", note = "use `FS::write_base()` instead")] +#[allow(clippy::missing_safety_doc)] #[inline] pub unsafe fn wrfsbase(val: u64) { - #[cfg(feature = "inline_asm")] - asm!("wrfsbase {}", in(reg) val, options(nostack, preserves_flags)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_wrfsbase(val); + FS::write_base(VirtAddr::new(val)) } - -/// Reads the FS segment base address -/// -/// ## Safety -/// -/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`. +/// Alias for [`FS::read_base()`] +#[deprecated(since = "0.14.4", note = "use `FS::read_base()` instead")] +#[allow(clippy::missing_safety_doc)] #[inline] pub unsafe fn rdfsbase() -> u64 { - #[cfg(feature = "inline_asm")] - { - let val: u64; - asm!("rdfsbase {}", out(reg) val, options(nomem, nostack, preserves_flags)); - val - } - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_rdfsbase() + FS::read_base().as_u64() } - -/// Writes the GS segment base address -/// -/// ## Safety -/// -/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`. +/// Alias for [`GS::write_base()`]. /// -/// The caller must ensure that this write operation has no unsafe side -/// effects, as the GS segment base address might be in use. +/// Panics if the provided address is non-canonical. +#[deprecated(since = "0.14.4", note = "use `GS::write_base()` instead")] +#[allow(clippy::missing_safety_doc)] #[inline] pub unsafe fn wrgsbase(val: u64) { - #[cfg(feature = "inline_asm")] - asm!("wrgsbase {}", in(reg) val, options(nostack, preserves_flags)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_wrgsbase(val); + GS::write_base(VirtAddr::new(val)) } - -/// Reads the GS segment base address -/// -/// ## Safety -/// -/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`. +/// Alias for [`GS::read_base()`] +#[deprecated(since = "0.14.4", note = "use `GS::read_base()` instead")] +#[allow(clippy::missing_safety_doc)] #[inline] pub unsafe fn rdgsbase() -> u64 { - #[cfg(feature = "inline_asm")] - { - let val: u64; - asm!("rdgsbase {}", out(reg) val, options(nomem, nostack, preserves_flags)); - val - } - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_rdgsbase() + GS::read_base().as_u64() } diff --git a/src/registers/control.rs b/src/registers/control.rs index c09dd0d7e..4dd54acd1 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -24,6 +24,8 @@ bitflags! { /// /// This flags allows lazily saving x87/MMX/SSE instructions on hardware context switches. const TASK_SWITCHED = 1 << 3; + /// Indicates support of 387DX math coprocessor instructions when set + const EXTENSION_TYPE = 1 << 4; /// Enables the native error reporting mechanism for x87 FPU errors. const NUMERIC_ERROR = 1 << 5; /// Controls whether supervisor-level writes to read-only pages are inhibited. @@ -114,14 +116,20 @@ bitflags! { const PCID = 1 << 17; /// Enables extendet processor state management instructions, including XGETBV and XSAVE. const OSXSAVE = 1 << 18; + /// When set, the `LOADIWKEY` instruction is available; additionally, if system firmware has activated the AES key locker instructions, register EBX of CPUID leaf 0x19, bit 0 (AESKLE) is set and the AES key locker instructions are enabled. See the [Intel Key Locker Specification](https://software.intel.com/content/www/us/en/develop/download/intel-key-locker-specification.html) for information on this feature. + const KEY_LOCKER = 1 << 19; /// Prevents the execution of instructions that reside in pages accessible by user-mode /// software when the processor is in supervisor-mode. const SUPERVISOR_MODE_EXECUTION_PROTECTION = 1 << 20; /// Enables restrictions for supervisor-mode software when reading data from user-mode /// pages. const SUPERVISOR_MODE_ACCESS_PREVENTION = 1 << 21; - /// Enables 4-level paging to associate each linear address with a protection key. - const PROTECTION_KEY = 1 << 22; + /// Enables 4-level and 5-level paging to associate each linear address with a protection key in user mode. + const PROTECTION_KEY_USER = 1 << 22; + /// When set, enables intel control-flow enforcement technology. See chapter 18 of the Intel software developer manuals, volume 1, for more information. + const CONTROL_FLOW_ENFORCEMENT = 1 << 23; + /// When set, allows 4-level and 5-level paging implementations to use the `IA32_PKRS` MSR to specify, for each protection key, whether supervisor-mode linear addresses with a particular protection key can be read or written. + const PROTECTION_KEY_SUPERVISOR = 1 << 24; } } diff --git a/src/registers/mod.rs b/src/registers/mod.rs index 81b668417..27762f9dc 100644 --- a/src/registers/mod.rs +++ b/src/registers/mod.rs @@ -6,6 +6,7 @@ pub mod rflags; pub mod xcontrol; #[cfg(feature = "instructions")] +#[allow(deprecated)] pub use crate::instructions::segmentation::{rdfsbase, rdgsbase, wrfsbase, wrgsbase}; #[cfg(all(feature = "instructions", feature = "inline_asm"))] diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index acebb1a5a..073a08c4f 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -1,5 +1,11 @@ //! Functions to read and write model specific registers. +#[cfg(docsrs)] +use crate::{ + instructions::segmentation::{Segment64, FS, GS}, + registers::control::Cr4Flags, +}; + use bitflags::bitflags; /// A model specific register. @@ -18,15 +24,19 @@ impl Msr { #[derive(Debug)] pub struct Efer; -/// FS.Base Model Specific Register. +/// [FS].Base Model Specific Register. #[derive(Debug)] pub struct FsBase; -/// GS.Base Model Specific Register. +/// [GS].Base Model Specific Register. +/// +/// [`GS::swap`] swaps this register with [`KernelGsBase`]. #[derive(Debug)] pub struct GsBase; /// KernelGsBase Model Specific Register. +/// +/// [`GS::swap`] swaps this register with [`GsBase`]. #[derive(Debug)] pub struct KernelGsBase; @@ -223,12 +233,18 @@ mod x86_64 { impl FsBase { /// Read the current FsBase register. + /// + /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is set, the more efficient + /// [`FS::read_base`] can be used instead. #[inline] pub fn read() -> VirtAddr { VirtAddr::new(unsafe { Self::MSR.read() }) } /// Write a given virtual address to the FS.Base register. + /// + /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is set, the more efficient + /// [`FS::write_base`] can be used instead. #[inline] pub fn write(address: VirtAddr) { let mut msr = Self::MSR; @@ -238,12 +254,18 @@ mod x86_64 { impl GsBase { /// Read the current GsBase register. + /// + /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is set, the more efficient + /// [`GS::read_base`] can be used instead. #[inline] pub fn read() -> VirtAddr { VirtAddr::new(unsafe { Self::MSR.read() }) } /// Write a given virtual address to the GS.Base register. + /// + /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is set, the more efficient + /// [`GS::write_base`] can be used instead. #[inline] pub fn write(address: VirtAddr) { let mut msr = Self::MSR; diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 8d2dfb789..53aca49c8 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -15,9 +15,19 @@ bitflags! { const SSE = 1<<1; /// Enables 256-bit SSE /// Must be set to enable AVX - const YMM = 1<<2; + const AVX = 1<<2; + /// When set, MPX instructions are enabled and the bound registers BND0-BND3 can be managed by XSAVE. + const BNDREG = 1 << 3; + /// When set, MPX instructions can be executed and XSAVE can manage the BNDCFGU and BNDSTATUS registers. + const BNDCSR = 1 << 4; + /// If set, AVX-512 instructions can be executed and XSAVE can manage the K0-K7 mask registers. + const OPMASK = 1 << 5; + /// If set, AVX-512 instructions can be executed and XSAVE can be used to manage the upper halves of the lower ZMM registers. + const ZMM_HI256 = 1 << 6; + /// If set, AVX-512 instructions can be executed and XSAVE can manage the upper ZMM registers. + const HI16_ZMM = 1 << 7; /// When set, PKRU state management is supported by - /// ZSAVE/XRSTOR + /// XSAVE/XRSTOR const MPK = 1<<9; /// When set the Lightweight Profiling extensions are enabled const LWP = 1<<62; @@ -68,6 +78,13 @@ mod x86_64 { 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"); + assert!((flags.contains(XCr0Flags::AVX) && flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)) || !(flags.contains(XCr0Flags::AVX) && flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)), "You must enable AVX to set or unset any of XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM"); + if !flags.contains(XCr0Flags::AVX) && (flags.contains(XCr0Flags::OPMASK) || flags.contains(XCr0Flags::ZMM_HI256) || flags.contains(XCr0Flags::HI16_ZMM)) { + panic!("You must have AVX enabled to set XCR0.opmask, XCR0.ZMM_Hi256, or XCR0.Hi16_ZMM"); + } + assert!((flags.contains(XCr0Flags::BNDREG) && flags.contains(XCr0Flags::BNDCSR)) || !(flags.contains(XCr0Flags::BNDREG) && flags.contains(XCr0Flags::BNDCSR)), "BNDREG and BNDCSR must be set and unset together"); + assert!((flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)) || !(flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)), "You must set or unset all of XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM"); Self::write_raw(new_value); } diff --git a/src/structures/idt.rs b/src/structures/idt.rs index e24aa3623..df76238a2 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -630,13 +630,13 @@ impl Entry { #[cfg(feature = "instructions")] #[inline] fn set_handler_addr(&mut self, addr: u64) -> &mut EntryOptions { - use crate::instructions::segmentation; + use crate::instructions::segmentation::{Segment, CS}; self.pointer_low = addr as u16; self.pointer_middle = (addr >> 16) as u16; self.pointer_high = (addr >> 32) as u32; - self.gdt_selector = segmentation::cs().0; + self.gdt_selector = CS::get_reg().0; self.options.set_present(true); &mut self.options diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index 3cfa6cc84..70f4df5cf 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -38,12 +38,12 @@ struct Selectors { } pub fn init() { - use x86_64::instructions::segmentation::set_cs; + use x86_64::instructions::segmentation::{CS, Segment}; use x86_64::instructions::tables::load_tss; GDT.0.load(); unsafe { - set_cs(GDT.1.code_selector); + CS::set_reg(GDT.1.code_selector); load_tss(GDT.1.tss_selector); } }