diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 82e408a91..a408654b6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -77,6 +77,12 @@ jobs: args: --no-default-features --features external_asm,instructions if: runner.os != 'Windows' + - name: "Run cargo doc without default features" + uses: actions-rs/cargo@v1 + with: + command: doc + args: --no-default-features + - name: "Run cargo build for stable without instructions" uses: actions-rs/cargo@v1 with: diff --git a/src/instructions/segmentation.rs b/src/instructions/segmentation.rs index 548a556da..bf16abe5a 100644 --- a/src/instructions/segmentation.rs +++ b/src/instructions/segmentation.rs @@ -1,64 +1,12 @@ //! Provides functions to read and write segment registers. -#[cfg(doc)] -use crate::{ - registers::control::Cr4Flags, - structures::gdt::{Descriptor, GlobalDescriptorTable}, -}; +pub use crate::registers::segmentation::{Segment, Segment64, CS, DS, ES, FS, GS, SS}; use crate::{ registers::model_specific::{FsBase, GsBase, Msr}, structures::gdt::SegmentSelector, VirtAddr, }; -/// An x86 segment -/// -/// 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 -/// -/// 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); -} - macro_rules! get_reg_impl { ($name:literal, $asm_get:ident) => { fn get_reg() -> SegmentSelector { @@ -120,14 +68,6 @@ macro_rules! segment64_impl { }; } -/// Code Segment -/// -/// 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); @@ -157,50 +97,11 @@ impl Segment for CS { } } -/// Stack Segment -/// -/// 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 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); - -/// 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); - -/// ES Segment -/// -/// 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 -/// -/// 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); - -/// 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); diff --git a/src/registers/control.rs b/src/registers/control.rs index 0c8c81e12..8d55d7147 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -1,9 +1,6 @@ //! Functions to read and write control registers. pub use super::model_specific::{Efer, EferFlags}; -#[cfg(doc)] -use crate::{registers::rflags::RFlags, structures::paging::PageTableFlags}; - use bitflags::bitflags; /// Various control flags modifying the basic operation of the CPU. diff --git a/src/registers/mod.rs b/src/registers/mod.rs index 27762f9dc..d569f5554 100644 --- a/src/registers/mod.rs +++ b/src/registers/mod.rs @@ -3,6 +3,7 @@ pub mod control; pub mod model_specific; pub mod rflags; +pub mod segmentation; pub mod xcontrol; #[cfg(feature = "instructions")] diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 191ace54a..aa1aa9b64 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -1,12 +1,9 @@ //! Functions to read and write model specific registers. -#[cfg(doc)] -use crate::{ - instructions::segmentation::{Segment64, FS, GS}, - registers::control::Cr4Flags, -}; - use bitflags::bitflags; +// imports for intra doc links +#[cfg(doc)] +use crate::registers::segmentation::{FS, GS}; /// A model specific register. #[derive(Debug)] @@ -30,13 +27,19 @@ pub struct FsBase; /// [GS].Base Model Specific Register. /// -/// [`GS::swap`] swaps this register with [`KernelGsBase`]. +#[cfg_attr( + feature = "instructions", + doc = "[`GS::swap`] swaps this register with [`KernelGsBase`]." +)] #[derive(Debug)] pub struct GsBase; /// KernelGsBase Model Specific Register. /// -/// [`GS::swap`] swaps this register with [`GsBase`]. +#[cfg_attr( + feature = "instructions", + doc = "[`GS::swap`] swaps this register with [`GsBase`]." +)] #[derive(Debug)] pub struct KernelGsBase; @@ -118,6 +121,12 @@ mod x86_64 { use crate::PrivilegeLevel; use bit_field::BitField; use core::convert::TryInto; + // imports for intra doc links + #[cfg(doc)] + use crate::registers::{ + control::Cr4Flags, + segmentation::{Segment, Segment64, CS, SS}, + }; impl Msr { /// Read 64 bits msr register. diff --git a/src/registers/segmentation.rs b/src/registers/segmentation.rs new file mode 100644 index 000000000..c62fff281 --- /dev/null +++ b/src/registers/segmentation.rs @@ -0,0 +1,155 @@ +//! Abstractions for segment registers. + +use super::model_specific::Msr; +use crate::{PrivilegeLevel, VirtAddr}; +use bit_field::BitField; +use core::fmt; +// imports for intra doc links +#[cfg(doc)] +use crate::registers::control::Cr4Flags; + +/// An x86 segment +/// +/// Segment registers on x86 are 16-bit [`SegmentSelector`]s, which index into +/// the [`GlobalDescriptorTable`](crate::structures::gdt::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 +/// +/// 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); +} + +/// Specifies which element to load into a segment from +/// descriptor tables (i.e., is a index to LDT or GDT table +/// with some additional flags). +/// +/// See Intel 3a, Section 3.4.2 "Segment Selectors" +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct SegmentSelector(pub u16); + +impl SegmentSelector { + /// Creates a new SegmentSelector + /// + /// # Arguments + /// * `index`: index in GDT or LDT array (not the offset) + /// * `rpl`: the requested privilege level + #[inline] + pub const fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector { + SegmentSelector(index << 3 | (rpl as u16)) + } + + /// Returns the GDT index. + #[inline] + pub fn index(self) -> u16 { + self.0 >> 3 + } + + /// Returns the requested privilege level. + #[inline] + pub fn rpl(self) -> PrivilegeLevel { + PrivilegeLevel::from_u16(self.0.get_bits(0..2)) + } + + /// Set the privilege level for this Segment selector. + #[inline] + pub fn set_rpl(&mut self, rpl: PrivilegeLevel) { + self.0.set_bits(0..2, rpl as u16); + } +} + +impl fmt::Debug for SegmentSelector { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut s = f.debug_struct("SegmentSelector"); + s.field("index", &self.index()); + s.field("rpl", &self.rpl()); + s.finish() + } +} + +/// Code Segment +/// +/// 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; + +/// Stack Segment +/// +/// 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`](crate::structures::gdt::Descriptor) (it cannot be zero). This +/// means a user-mode read/write segment descriptor must be present in the GDT. +/// +/// 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; + +/// Data Segment +/// +/// Entirely unused in 64-bit mode; setting the segment register does nothing. +#[derive(Debug)] +pub struct DS; + +/// ES Segment +/// +/// Entirely unused in 64-bit mode; setting the segment register does nothing. +#[derive(Debug)] +pub struct ES; + +/// FS Segment +/// +/// 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; + +/// 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; diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 38cc965e2..4911146a8 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -1,60 +1,13 @@ //! Types for the Global Descriptor Table and segment selectors. -#[cfg(doc)] -use crate::instructions::segmentation::{Segment, CS, SS}; +pub use crate::registers::segmentation::SegmentSelector; use crate::structures::tss::TaskStateSegment; use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; -use core::fmt; - -/// Specifies which element to load into a segment from -/// descriptor tables (i.e., is a index to LDT or GDT table -/// with some additional flags). -/// -/// See Intel 3a, Section 3.4.2 "Segment Selectors" -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(transparent)] -pub struct SegmentSelector(pub u16); - -impl SegmentSelector { - /// Creates a new SegmentSelector - /// - /// # Arguments - /// * `index`: index in GDT or LDT array (not the offset) - /// * `rpl`: the requested privilege level - #[inline] - pub const fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector { - SegmentSelector(index << 3 | (rpl as u16)) - } - - /// Returns the GDT index. - #[inline] - pub fn index(self) -> u16 { - self.0 >> 3 - } - - /// Returns the requested privilege level. - #[inline] - pub fn rpl(self) -> PrivilegeLevel { - PrivilegeLevel::from_u16(self.0.get_bits(0..2)) - } - - /// Set the privilege level for this Segment selector. - #[inline] - pub fn set_rpl(&mut self, rpl: PrivilegeLevel) { - self.0.set_bits(0..2, rpl as u16); - } -} - -impl fmt::Debug for SegmentSelector { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut s = f.debug_struct("SegmentSelector"); - s.field("index", &self.index()); - s.field("rpl", &self.rpl()); - s.finish() - } -} +// imports for intra-doc links +#[cfg(doc)] +use crate::registers::segmentation::{Segment, CS, SS}; /// A 64-bit mode global descriptor table (GDT). ///