x86_64/registers/segmentation.rs
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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
//! 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,
structures::gdt::{Descriptor, DescriptorFlags, GlobalDescriptorTable},
};
/// 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);
}
/// 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, PartialOrd, Ord, Hash)]
#[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))
}
/// Can be used as a selector into a non-existent segment and assigned to segment registers,
/// e.g. data segment register in ring 0
pub const NULL: Self = Self::new(0, PrivilegeLevel::Ring0);
/// 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
///
/// While most fields in the Code-Segment [`Descriptor`] are unused in 64-bit
/// long mode, some of them must be set to a specific value. The
/// [`EXECUTABLE`](DescriptorFlags::EXECUTABLE),
/// [`USER_SEGMENT`](DescriptorFlags::USER_SEGMENT), and
/// [`LONG_MODE`](DescriptorFlags::LONG_MODE) bits must be set, while the
/// [`DEFAULT_SIZE`](DescriptorFlags::DEFAULT_SIZE) bit must be unset.
///
/// The [`DPL_RING_3`](DescriptorFlags::DPL_RING_3) field can be used to change
/// privilege level. The [`PRESENT`](DescriptorFlags::PRESENT) bit can be used
/// to make a segment present or not present.
///
/// All other fields (like the segment base and limit) are ignored by the
/// processor and setting them has no effect.
#[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`] (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;