Skip to content

Commit 022598a

Browse files
authored
Merge pull request #309 from rust-osdev/fix-307
Move segment types into a new `registers::segmentation` module
2 parents 05b5f4f + a280bbe commit 022598a

File tree

7 files changed

+184
-162
lines changed

7 files changed

+184
-162
lines changed

.github/workflows/build.yml

+6
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ jobs:
7777
args: --no-default-features --features external_asm,instructions
7878
if: runner.os != 'Windows'
7979

80+
- name: "Run cargo doc without default features"
81+
uses: actions-rs/cargo@v1
82+
with:
83+
command: doc
84+
args: --no-default-features
85+
8086
- name: "Run cargo build for stable without instructions"
8187
uses: actions-rs/cargo@v1
8288
with:

src/instructions/segmentation.rs

+1-100
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,12 @@
11
//! Provides functions to read and write segment registers.
22
3-
#[cfg(doc)]
4-
use crate::{
5-
registers::control::Cr4Flags,
6-
structures::gdt::{Descriptor, GlobalDescriptorTable},
7-
};
3+
pub use crate::registers::segmentation::{Segment, Segment64, CS, DS, ES, FS, GS, SS};
84
use crate::{
95
registers::model_specific::{FsBase, GsBase, Msr},
106
structures::gdt::SegmentSelector,
117
VirtAddr,
128
};
139

14-
/// An x86 segment
15-
///
16-
/// Segment registers on x86 are 16-bit [`SegmentSelector`]s, which index into
17-
/// the [`GlobalDescriptorTable`]. The corresponding GDT entry is used to
18-
/// configure the segment itself. Note that most segmentation functionality is
19-
/// disabled in 64-bit mode. See the individual segments for more information.
20-
pub trait Segment {
21-
/// Returns the current value of the segment register.
22-
fn get_reg() -> SegmentSelector;
23-
/// Reload the segment register. Depending on the segment, this may also
24-
/// reconfigure the corresponding segment.
25-
///
26-
/// ## Safety
27-
///
28-
/// This function is unsafe because the caller must ensure that `sel`
29-
/// is a valid segment descriptor, and that reconfiguring the segment will
30-
/// not cause undefined behavior.
31-
unsafe fn set_reg(sel: SegmentSelector);
32-
}
33-
34-
/// An x86 segment which is actually used in 64-bit mode
35-
///
36-
/// While most segments are unused in 64-bit mode, the FS and GS segments are
37-
/// still partially used. Only the 64-bit segment base address is used, and this
38-
/// address can be set via the GDT, or by using the `FSGSBASE` instructions.
39-
pub trait Segment64: Segment {
40-
/// MSR containing the segment base. This MSR can be used to set the base
41-
/// when [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is **not** set.
42-
const BASE: Msr;
43-
/// Reads the segment base address
44-
///
45-
/// ## Exceptions
46-
///
47-
/// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`.
48-
fn read_base() -> VirtAddr;
49-
/// Writes the segment base address
50-
///
51-
/// ## Exceptions
52-
///
53-
/// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`.
54-
///
55-
/// ## Safety
56-
///
57-
/// The caller must ensure that this write operation has no unsafe side
58-
/// effects, as the segment base address might be in use.
59-
unsafe fn write_base(base: VirtAddr);
60-
}
61-
6210
macro_rules! get_reg_impl {
6311
($name:literal, $asm_get:ident) => {
6412
fn get_reg() -> SegmentSelector {
@@ -120,14 +68,6 @@ macro_rules! segment64_impl {
12068
};
12169
}
12270

123-
/// Code Segment
124-
///
125-
/// The segment base and limit are unused in 64-bit mode. Only the L (long), D
126-
/// (default operation size), and DPL (descriptor privilege-level) fields of the
127-
/// descriptor are recognized. So changing the segment register can be used to
128-
/// change privilege level or enable/disable long mode.
129-
#[derive(Debug)]
130-
pub struct CS;
13171
impl Segment for CS {
13272
get_reg_impl!("cs", x86_64_asm_get_cs);
13373

@@ -157,50 +97,11 @@ impl Segment for CS {
15797
}
15898
}
15999

160-
/// Stack Segment
161-
///
162-
/// Entirely unused in 64-bit mode; setting the segment register does nothing.
163-
/// However, in ring 3, the SS register still has to point to a valid
164-
/// [`Descriptor`] (it cannot be zero). This means a user-mode read/write
165-
/// segment descriptor must be present in the GDT.
166-
///
167-
/// This register is also set by the `syscall`/`sysret` and
168-
/// `sysenter`/`sysexit` instructions (even on 64-bit transitions). This is to
169-
/// maintain symmetry with 32-bit transitions where setting SS actually will
170-
/// actually have an effect.
171-
#[derive(Debug)]
172-
pub struct SS;
173100
segment_impl!(SS, "ss", x86_64_asm_get_ss, x86_64_asm_load_ss);
174-
175-
/// Data Segment
176-
///
177-
/// Entirely unused in 64-bit mode; setting the segment register does nothing.
178-
#[derive(Debug)]
179-
pub struct DS;
180101
segment_impl!(DS, "ds", x86_64_asm_get_ds, x86_64_asm_load_ds);
181-
182-
/// ES Segment
183-
///
184-
/// Entirely unused in 64-bit mode; setting the segment register does nothing.
185-
#[derive(Debug)]
186-
pub struct ES;
187102
segment_impl!(ES, "es", x86_64_asm_get_es, x86_64_asm_load_es);
188-
189-
/// FS Segment
190-
///
191-
/// Only base is used in 64-bit mode, see [`Segment64`]. This is often used in
192-
/// user-mode for Thread-Local Storage (TLS).
193-
#[derive(Debug)]
194-
pub struct FS;
195103
segment_impl!(FS, "fs", x86_64_asm_get_fs, x86_64_asm_load_fs);
196104
segment64_impl!(FS, "fs", FsBase, x86_64_asm_rdfsbase, x86_64_asm_wrfsbase);
197-
198-
/// GS Segment
199-
///
200-
/// Only base is used in 64-bit mode, see [`Segment64`]. In kernel-mode, the GS
201-
/// base often points to a per-cpu kernel data structure.
202-
#[derive(Debug)]
203-
pub struct GS;
204105
segment_impl!(GS, "gs", x86_64_asm_get_gs, x86_64_asm_load_gs);
205106
segment64_impl!(GS, "gs", GsBase, x86_64_asm_rdgsbase, x86_64_asm_wrgsbase);
206107

src/registers/control.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
//! Functions to read and write control registers.
22
33
pub use super::model_specific::{Efer, EferFlags};
4-
#[cfg(doc)]
5-
use crate::{registers::rflags::RFlags, structures::paging::PageTableFlags};
6-
74
use bitflags::bitflags;
85

96
/// Various control flags modifying the basic operation of the CPU.

src/registers/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pub mod control;
44
pub mod model_specific;
55
pub mod rflags;
6+
pub mod segmentation;
67
pub mod xcontrol;
78

89
#[cfg(feature = "instructions")]

src/registers/model_specific.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
//! Functions to read and write model specific registers.
22
3-
#[cfg(doc)]
4-
use crate::{
5-
instructions::segmentation::{Segment64, FS, GS},
6-
registers::control::Cr4Flags,
7-
};
8-
93
use bitflags::bitflags;
4+
// imports for intra doc links
5+
#[cfg(doc)]
6+
use crate::registers::segmentation::{FS, GS};
107

118
/// A model specific register.
129
#[derive(Debug)]
@@ -30,13 +27,19 @@ pub struct FsBase;
3027

3128
/// [GS].Base Model Specific Register.
3229
///
33-
/// [`GS::swap`] swaps this register with [`KernelGsBase`].
30+
#[cfg_attr(
31+
feature = "instructions",
32+
doc = "[`GS::swap`] swaps this register with [`KernelGsBase`]."
33+
)]
3434
#[derive(Debug)]
3535
pub struct GsBase;
3636

3737
/// KernelGsBase Model Specific Register.
3838
///
39-
/// [`GS::swap`] swaps this register with [`GsBase`].
39+
#[cfg_attr(
40+
feature = "instructions",
41+
doc = "[`GS::swap`] swaps this register with [`GsBase`]."
42+
)]
4043
#[derive(Debug)]
4144
pub struct KernelGsBase;
4245

@@ -118,6 +121,12 @@ mod x86_64 {
118121
use crate::PrivilegeLevel;
119122
use bit_field::BitField;
120123
use core::convert::TryInto;
124+
// imports for intra doc links
125+
#[cfg(doc)]
126+
use crate::registers::{
127+
control::Cr4Flags,
128+
segmentation::{Segment, Segment64, CS, SS},
129+
};
121130

122131
impl Msr {
123132
/// Read 64 bits msr register.

src/registers/segmentation.rs

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//! Abstractions for segment registers.
2+
3+
use super::model_specific::Msr;
4+
use crate::{PrivilegeLevel, VirtAddr};
5+
use bit_field::BitField;
6+
use core::fmt;
7+
// imports for intra doc links
8+
#[cfg(doc)]
9+
use crate::registers::control::Cr4Flags;
10+
11+
/// An x86 segment
12+
///
13+
/// Segment registers on x86 are 16-bit [`SegmentSelector`]s, which index into
14+
/// the [`GlobalDescriptorTable`](crate::structures::gdt::GlobalDescriptorTable). The
15+
/// corresponding GDT entry is used to
16+
/// configure the segment itself. Note that most segmentation functionality is
17+
/// disabled in 64-bit mode. See the individual segments for more information.
18+
pub trait Segment {
19+
/// Returns the current value of the segment register.
20+
fn get_reg() -> SegmentSelector;
21+
/// Reload the segment register. Depending on the segment, this may also
22+
/// reconfigure the corresponding segment.
23+
///
24+
/// ## Safety
25+
///
26+
/// This function is unsafe because the caller must ensure that `sel`
27+
/// is a valid segment descriptor, and that reconfiguring the segment will
28+
/// not cause undefined behavior.
29+
unsafe fn set_reg(sel: SegmentSelector);
30+
}
31+
32+
/// An x86 segment which is actually used in 64-bit mode
33+
///
34+
/// While most segments are unused in 64-bit mode, the FS and GS segments are
35+
/// still partially used. Only the 64-bit segment base address is used, and this
36+
/// address can be set via the GDT, or by using the `FSGSBASE` instructions.
37+
pub trait Segment64: Segment {
38+
/// MSR containing the segment base. This MSR can be used to set the base
39+
/// when [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is **not** set.
40+
const BASE: Msr;
41+
/// Reads the segment base address
42+
///
43+
/// ## Exceptions
44+
///
45+
/// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`.
46+
fn read_base() -> VirtAddr;
47+
/// Writes the segment base address
48+
///
49+
/// ## Exceptions
50+
///
51+
/// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`.
52+
///
53+
/// ## Safety
54+
///
55+
/// The caller must ensure that this write operation has no unsafe side
56+
/// effects, as the segment base address might be in use.
57+
unsafe fn write_base(base: VirtAddr);
58+
}
59+
60+
/// Specifies which element to load into a segment from
61+
/// descriptor tables (i.e., is a index to LDT or GDT table
62+
/// with some additional flags).
63+
///
64+
/// See Intel 3a, Section 3.4.2 "Segment Selectors"
65+
#[derive(Clone, Copy, PartialEq, Eq)]
66+
#[repr(transparent)]
67+
pub struct SegmentSelector(pub u16);
68+
69+
impl SegmentSelector {
70+
/// Creates a new SegmentSelector
71+
///
72+
/// # Arguments
73+
/// * `index`: index in GDT or LDT array (not the offset)
74+
/// * `rpl`: the requested privilege level
75+
#[inline]
76+
pub const fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector {
77+
SegmentSelector(index << 3 | (rpl as u16))
78+
}
79+
80+
/// Returns the GDT index.
81+
#[inline]
82+
pub fn index(self) -> u16 {
83+
self.0 >> 3
84+
}
85+
86+
/// Returns the requested privilege level.
87+
#[inline]
88+
pub fn rpl(self) -> PrivilegeLevel {
89+
PrivilegeLevel::from_u16(self.0.get_bits(0..2))
90+
}
91+
92+
/// Set the privilege level for this Segment selector.
93+
#[inline]
94+
pub fn set_rpl(&mut self, rpl: PrivilegeLevel) {
95+
self.0.set_bits(0..2, rpl as u16);
96+
}
97+
}
98+
99+
impl fmt::Debug for SegmentSelector {
100+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101+
let mut s = f.debug_struct("SegmentSelector");
102+
s.field("index", &self.index());
103+
s.field("rpl", &self.rpl());
104+
s.finish()
105+
}
106+
}
107+
108+
/// Code Segment
109+
///
110+
/// The segment base and limit are unused in 64-bit mode. Only the L (long), D
111+
/// (default operation size), and DPL (descriptor privilege-level) fields of the
112+
/// descriptor are recognized. So changing the segment register can be used to
113+
/// change privilege level or enable/disable long mode.
114+
#[derive(Debug)]
115+
pub struct CS;
116+
117+
/// Stack Segment
118+
///
119+
/// Entirely unused in 64-bit mode; setting the segment register does nothing.
120+
/// However, in ring 3, the SS register still has to point to a valid
121+
/// [`Descriptor`](crate::structures::gdt::Descriptor) (it cannot be zero). This
122+
/// means a user-mode read/write segment descriptor must be present in the GDT.
123+
///
124+
/// This register is also set by the `syscall`/`sysret` and
125+
/// `sysenter`/`sysexit` instructions (even on 64-bit transitions). This is to
126+
/// maintain symmetry with 32-bit transitions where setting SS actually will
127+
/// actually have an effect.
128+
#[derive(Debug)]
129+
pub struct SS;
130+
131+
/// Data Segment
132+
///
133+
/// Entirely unused in 64-bit mode; setting the segment register does nothing.
134+
#[derive(Debug)]
135+
pub struct DS;
136+
137+
/// ES Segment
138+
///
139+
/// Entirely unused in 64-bit mode; setting the segment register does nothing.
140+
#[derive(Debug)]
141+
pub struct ES;
142+
143+
/// FS Segment
144+
///
145+
/// Only base is used in 64-bit mode, see [`Segment64`]. This is often used in
146+
/// user-mode for Thread-Local Storage (TLS).
147+
#[derive(Debug)]
148+
pub struct FS;
149+
150+
/// GS Segment
151+
///
152+
/// Only base is used in 64-bit mode, see [`Segment64`]. In kernel-mode, the GS
153+
/// base often points to a per-cpu kernel data structure.
154+
#[derive(Debug)]
155+
pub struct GS;

0 commit comments

Comments
 (0)