Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move segment types into a new registers::segmentation module #309

Merged
merged 1 commit into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
101 changes: 1 addition & 100 deletions src/instructions/segmentation.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
3 changes: 0 additions & 3 deletions src/registers/control.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
1 change: 1 addition & 0 deletions src/registers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod control;
pub mod model_specific;
pub mod rflags;
pub mod segmentation;
pub mod xcontrol;

#[cfg(feature = "instructions")]
Expand Down
25 changes: 17 additions & 8 deletions src/registers/model_specific.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -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;

Expand Down Expand Up @@ -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.
Expand Down
155 changes: 155 additions & 0 deletions src/registers/segmentation.rs
Original file line number Diff line number Diff line change
@@ -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;
Loading