x86/bits64/
segmentation.rs

1#[allow(unused_imports)]
2use crate::segmentation::SegmentSelector;
3use crate::segmentation::{
4    BuildDescriptor, Descriptor, DescriptorBuilder, DescriptorType, GateDescriptorBuilder,
5    LdtDescriptorBuilder, SystemDescriptorTypes64,
6};
7
8#[cfg(target_arch = "x86_64")]
9use core::arch::asm;
10
11/// Entry for IDT, GDT or LDT.
12///
13/// See Intel 3a, Section 3.4.5 "Segment Descriptors", and Section 3.5.2
14/// "Segment Descriptor Tables in IA-32e Mode", especially Figure 3-8.
15#[derive(Copy, Clone, Debug, Default)]
16#[repr(C, packed)]
17pub struct Descriptor64 {
18    desc32: Descriptor,
19    lower: u32,
20    upper: u32,
21}
22
23impl Descriptor64 {
24    pub const NULL: Descriptor64 = Descriptor64 {
25        desc32: Descriptor::NULL,
26        lower: 0,
27        upper: 0,
28    };
29
30    pub(crate) fn apply_builder_settings(&mut self, builder: &DescriptorBuilder) {
31        self.desc32.apply_builder_settings(builder);
32        if let Some((base, limit)) = builder.base_limit {
33            self.set_base_limit(base, limit)
34        }
35        if let Some((selector, offset)) = builder.selector_offset {
36            self.set_selector_offset(selector, offset)
37        }
38    }
39
40    /// Create a new segment, TSS or LDT descriptor
41    /// by setting the three base and two limit fields.
42    pub fn set_base_limit(&mut self, base: u64, limit: u64) {
43        self.desc32.set_base_limit(base as u32, limit as u32);
44        self.lower = (base >> 32) as u32;
45    }
46
47    /// Creates a new descriptor with selector and offset (for IDT Gate descriptors,
48    /// e.g. Trap, Interrupts and Task gates)
49    pub fn set_selector_offset(&mut self, selector: SegmentSelector, offset: u64) {
50        self.desc32.set_selector_offset(selector, offset as u32);
51        self.lower = (offset >> 32) as u32;
52    }
53
54    /// Sets the interrupt stack table index.
55    /// The 3-bit IST index field that provides an offset into the IST section of the TSS.
56    /// Using the IST mechanism, the processor loads the value pointed by an IST pointer into the RSP.
57    pub fn set_ist(&mut self, index: u8) {
58        assert!(index <= 0b111);
59        self.desc32.upper |= index as u32;
60    }
61}
62
63impl GateDescriptorBuilder<u64> for DescriptorBuilder {
64    fn tss_descriptor(base: u64, limit: u64, available: bool) -> DescriptorBuilder {
65        let typ = if available {
66            DescriptorType::System64(SystemDescriptorTypes64::TssAvailable)
67        } else {
68            DescriptorType::System64(SystemDescriptorTypes64::TssBusy)
69        };
70
71        DescriptorBuilder::with_base_limit(base, limit).set_type(typ)
72    }
73
74    fn call_gate_descriptor(selector: SegmentSelector, offset: u64) -> DescriptorBuilder {
75        DescriptorBuilder::with_selector_offset(selector, offset)
76            .set_type(DescriptorType::System64(SystemDescriptorTypes64::CallGate))
77    }
78
79    fn interrupt_descriptor(selector: SegmentSelector, offset: u64) -> DescriptorBuilder {
80        DescriptorBuilder::with_selector_offset(selector, offset).set_type(
81            DescriptorType::System64(SystemDescriptorTypes64::InterruptGate),
82        )
83    }
84
85    fn trap_gate_descriptor(selector: SegmentSelector, offset: u64) -> DescriptorBuilder {
86        DescriptorBuilder::with_selector_offset(selector, offset)
87            .set_type(DescriptorType::System64(SystemDescriptorTypes64::TrapGate))
88    }
89}
90
91impl LdtDescriptorBuilder<u64> for DescriptorBuilder {
92    fn ldt_descriptor(base: u64, limit: u64) -> DescriptorBuilder {
93        DescriptorBuilder::with_base_limit(base, limit)
94            .set_type(DescriptorType::System64(SystemDescriptorTypes64::LDT))
95    }
96}
97
98impl BuildDescriptor<Descriptor64> for DescriptorBuilder {
99    fn finish(&self) -> Descriptor64 {
100        let mut desc: Descriptor64 = Default::default();
101        desc.apply_builder_settings(self);
102
103        let typ = match self.typ {
104            Some(DescriptorType::System64(typ)) => {
105                assert!(!self.l);
106                if typ == SystemDescriptorTypes64::LDT
107                    || typ == SystemDescriptorTypes64::TssAvailable
108                    || typ == SystemDescriptorTypes64::TssBusy
109                {
110                    assert!(!self.db);
111                }
112
113                if typ == SystemDescriptorTypes64::InterruptGate {
114                    desc.set_ist(self.ist);
115                }
116
117                typ as u8
118            }
119            Some(DescriptorType::System32(_typ)) => {
120                panic!("Can't build a 64-bit version of this type.")
121            }
122            Some(DescriptorType::Data(_typ)) => {
123                panic!("Can't build a 64-bit version of this type.")
124            }
125            Some(DescriptorType::Code(_typ)) => {
126                panic!("Can't build a 64-bit version of this type.")
127            }
128            None => unreachable!("Type not set, this is a library bug in x86."),
129        };
130
131        desc.desc32.set_type(typ);
132        desc
133    }
134}
135
136/// Reload code segment register.
137///
138/// Note this is special since we can not directly move
139/// to %cs. Instead we push the new segment selector
140/// and return value on the stack and use lretq
141/// to reload cs and continue at 1:.
142///
143/// # Safety
144/// Can cause a GP-fault with a bad `sel` value.
145#[cfg(target_arch = "x86_64")]
146pub unsafe fn load_cs(sel: SegmentSelector) {
147    asm!("pushq {0}; \
148          leaq  1f(%rip), %rax; \
149          pushq %rax; \
150          lretq; \
151          1:", in(reg) sel.bits() as usize, out("rax") _, options(att_syntax));
152}
153
154/// Write GS Segment Base
155///
156/// # Safety
157/// Needs FSGSBASE-Enable Bit (bit 16 of CR4) set.
158#[cfg(target_arch = "x86_64")]
159pub unsafe fn wrgsbase(base: u64) {
160    asm!("wrgsbase {0}", in(reg) base, options(att_syntax));
161}
162
163/// Write FS Segment Base
164///
165/// # Safety
166/// Needs FSGSBASE-Enable Bit (bit 16 of CR4) set.
167#[cfg(target_arch = "x86_64")]
168pub unsafe fn wrfsbase(base: u64) {
169    asm!("wrfsbase {0}", in(reg) base, options(att_syntax));
170}
171
172/// Read GS Segment Base
173///
174/// # Safety
175/// Needs FSGSBASE-Enable Bit (bit 16 of CR4) set.
176#[cfg(target_arch = "x86_64")]
177pub unsafe fn rdgsbase() -> u64 {
178    let gs_base: u64;
179    asm!("rdgsbase {0}", out(reg) gs_base, options(att_syntax));
180    gs_base
181}
182
183/// Read FS Segment Base
184///
185/// # Safety
186/// Needs FSGSBASE-Enable Bit (bit 16 of CR4) set.
187#[cfg(target_arch = "x86_64")]
188pub unsafe fn rdfsbase() -> u64 {
189    let fs_base: u64;
190    asm!("rdfsbase {0}", out(reg) fs_base, options(att_syntax));
191    fs_base
192}
193
194/// "Dereferences" the fs register at `offset`.
195///
196/// # Safety
197/// - Offset needs to be within valid memory relative to what the fs register
198///   points to.
199#[cfg(target_arch = "x86_64")]
200#[macro_export]
201macro_rules! fs_deref {
202    ($offset:expr) => {{
203        let fs: u64;
204        core::arch::asm!("movq %fs:{offset}, {result}",
205                offset = const ($offset),
206                result = out(reg) fs,
207                options(att_syntax)
208        );
209        fs
210    }};
211}
212
213#[cfg(target_arch = "x86_64")]
214pub use fs_deref;
215
216/// "Dereferences" the gs register at `offset`.
217///
218/// # Safety
219/// - Offset needs to be within valid memory relative to what the gs register
220///   points to.
221#[cfg(target_arch = "x86_64")]
222#[macro_export]
223macro_rules! gs_deref {
224    ($offset:expr) => {{
225        let gs: u64;
226        core::arch::asm!("movq %gs:{offset}, {result}",
227                offset = const ($offset),
228                result = out(reg) gs,
229                options(att_syntax)
230        );
231        gs
232    }};
233}
234
235#[cfg(target_arch = "x86_64")]
236pub use gs_deref;
237
238/// Swap the GS register.
239///
240/// Exchanges the current GS base register value with the value contained
241/// in MSR address IA32_KERNEL_GS_BASE.
242///
243/// The SWAPGS instruction is available only in 64-bit mode.
244///
245/// # Safety
246/// The SWAPGS instruction is a privileged instruction intended for use by system software.
247#[cfg(target_arch = "x86_64")]
248pub unsafe fn swapgs() {
249    asm!("swapgs");
250}