x86/registers/
segmentation.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5// This is inspired and adapted for Tock from the [x86](https://github.com/gz/rust-x86) crate.
6
7use super::ring::Ring;
8use kernel::utilities::registers::register_bitfields;
9use tock_registers::LocalRegisterCopy;
10
11#[cfg(target_arch = "x86")]
12use core::arch::asm;
13
14macro_rules! bit {
15    ($x:expr) => {
16        1 << $x
17    };
18}
19
20register_bitfields![u16,
21    /// Specifies which element to load into a segment from
22    /// descriptor tables (i.e., is a index to LDT or GDT table
23    /// with some additional flags).
24    ///
25    /// See Intel 3a, Section 3.4.2 "Segment Selectors"
26    pub SEGMENT_SELECTOR[
27        RPL OFFSET(0) NUMBITS(2) [],
28        TI OFFSET (2) NUMBITS(1) [
29            GDT = 0,
30            LDT = 1
31        ],
32        INDEX OFFSET (3) NUMBITS (12) []
33    ],
34];
35
36#[repr(transparent)]
37#[derive(Copy, Clone, Debug)]
38pub struct SegmentSelector(LocalRegisterCopy<u16, SEGMENT_SELECTOR::Register>);
39
40impl SegmentSelector {
41    /// Create a new SegmentSelector
42    /// # Arguments
43    ///  * `index` - index in GDT or LDT array.
44    ///  * `rpl` - Requested privilege level of the selector
45    pub const fn new(index: u16, rpl: Ring) -> SegmentSelector {
46        SegmentSelector(LocalRegisterCopy::new(index << 3 | (rpl as u16)))
47    }
48
49    /// Returns segment selector's index in GDT or LDT.
50    pub fn index(&self) -> u16 {
51        self.0.get() >> 3
52    }
53
54    /// Make a new segment selector from a untyped u16 value.
55    pub fn from_raw(bits: u16) -> SegmentSelector {
56        SegmentSelector(LocalRegisterCopy::new(bits))
57    }
58
59    pub fn bits(&self) -> u16 {
60        self.0.get()
61    }
62}
63
64/// Entry for IDT, GDT or LDT. Provides size and location of a segment.
65/// See Intel 3a, Section 3.4.5 "Segment Descriptors", and Section 3.5.2
66#[derive(Copy, Clone, Debug, Default)]
67#[repr(C, packed)]
68pub struct Descriptor {
69    pub lower: u32,
70    pub upper: u32,
71}
72
73impl Descriptor {
74    pub const NULL: Descriptor = Descriptor { lower: 0, upper: 0 };
75
76    pub(crate) fn apply_builder_settings(&mut self, builder: &DescriptorBuilder) {
77        if let Some(ring) = builder.dpl {
78            self.set_dpl(ring)
79        }
80
81        if let Some((base, limit)) = builder.base_limit {
82            self.set_base_limit(base as u32, limit as u32)
83        }
84
85        if let Some((selector, offset)) = builder.selector_offset {
86            self.set_selector_offset(selector, offset as u32)
87        }
88
89        if builder.present {
90            self.set_p();
91        }
92        if builder.avl {
93            self.set_avl();
94        }
95        if builder.db {
96            self.set_db();
97        }
98        if builder.limit_granularity_4k {
99            self.set_g();
100        }
101        if builder.l {
102            self.set_l();
103        }
104    }
105
106    /// Specifies the privilege level of the segment. The DPL is used to control access to the segment.
107
108    pub fn set_dpl(&mut self, ring: Ring) {
109        assert!(ring as u32 <= 0b11);
110        self.upper &= !(0b11 << 13);
111        self.upper |= (ring as u32) << 13;
112    }
113
114    pub fn set_base_limit(&mut self, base: u32, limit: u32) {
115        // Clear the base and limit fields in Descriptor
116        self.lower = 0;
117        self.upper &= 0x00F0FF00;
118
119        // Set the new base
120        self.lower |= base << 16;
121        self.upper |= (base >> 16) & 0xff;
122        self.upper |= (base >> 24) << 24;
123
124        // Set the new limit
125        self.lower |= limit & 0xffff;
126        let limit_last_four_bits = (limit >> 16) & 0x0f;
127        self.upper |= limit_last_four_bits << 16;
128    }
129
130    /// Creates a new descriptor with selector and offset (for IDT Gate descriptors,
131
132    /// e.g. Trap, Interrupts and Task gates)
133
134    pub fn set_selector_offset(&mut self, selector: SegmentSelector, offset: u32) {
135        // Clear the selector and offset
136        self.lower = 0;
137        self.upper &= 0x0000ffff;
138
139        // Set selector
140        self.lower |= (selector.bits() as u32) << 16;
141
142        // Set offset
143        self.lower |= offset & 0x0000ffff;
144        self.upper |= offset & 0xffff0000;
145    }
146
147    /// Set the type of the descriptor (bits 8-11).
148    /// Indicates the segment or gate type and specifies the kinds of access that can be made to the
149    /// segment and the direction of growth. The interpretation of this field depends on whether the descriptor
150    /// type flag specifies an application (code or data) descriptor or a system descriptor.
151    pub fn set_type(&mut self, typ: u8) {
152        self.upper &= !(0x0f << 8); // clear
153        self.upper |= (typ as u32 & 0x0f) << 8;
154    }
155
156    /// Set Present bit.
157    /// Indicates whether the segment is present in memory (set) or not present (clear).
158    /// If this flag is clear, the processor generates a segment-not-present exception (#NP) when a segment selector
159    /// that points to the segment descriptor is loaded into a segment register.
160    pub fn set_p(&mut self) {
161        self.upper |= bit!(15);
162    }
163
164    /// Set AVL bit. System software can use this bit to store information.
165    pub fn set_avl(&mut self) {
166        self.upper |= bit!(20);
167    }
168
169    /// Set D/B.
170    /// Performs different functions depending on whether the segment descriptor is an executable code segment,
171    /// an expand-down data segment, or a stack segment.
172    pub fn set_db(&mut self) {
173        self.upper |= bit!(22);
174    }
175
176    /// Set G bit
177    /// Determines the scaling of the segment limit field.
178    /// When the granularity flag is clear, the segment limit is interpreted in byte units;
179    /// when flag is set, the segment limit is interpreted in 4-KByte units.
180    pub fn set_g(&mut self) {
181        self.upper |= bit!(23);
182    }
183
184    /// Set L
185    /// In IA-32e mode, bit 21 of the second doubleword of the segment descriptor indicates whether a
186    /// code segment contains native 64-bit code. A value of 1 indicates instructions in this code
187    /// segment are executed in 64-bit mode. A value of 0 indicates the instructions in this code segment
188    /// are executed in compatibility mode. If L-bit is set, then D-bit must be cleared.
189    pub fn set_l(&mut self) {
190        self.upper |= bit!(21);
191    }
192
193    /// Specifies whether the segment descriptor is for a system segment (S flag is clear) or a code or data segment (S flag is set).
194    pub fn set_s(&mut self) {
195        self.upper |= bit!(12);
196    }
197}
198
199pub trait BuildDescriptor<Descriptor> {
200    fn finish(&self) -> Descriptor;
201}
202
203impl BuildDescriptor<Descriptor> for DescriptorBuilder {
204    fn finish(&self) -> Descriptor {
205        let mut desc = Descriptor::default();
206        desc.apply_builder_settings(self);
207        let typ = match self.typ {
208            Some(DescriptorType::System32(typ)) => typ as u8,
209            Some(DescriptorType::Data(typ)) => {
210                desc.set_s();
211                typ as u8
212            }
213            Some(DescriptorType::Code(typ)) => {
214                desc.set_s();
215                typ as u8
216            }
217            None => unreachable!("Type not set, this is a library bug in x86."),
218        };
219        desc.set_type(typ);
220        desc
221    }
222}
223
224/// Code Segment types for descriptors.
225/// See also Intel 3a, Table 3-1 Code- and Data-Segment Types.
226#[repr(u8)]
227#[derive(Copy, Clone, Debug, Eq, PartialEq)]
228pub enum CodeSegmentType {
229    /// Code Execute-Only
230    Execute = 0b1000,
231
232    /// Code Execute-Only, accessed
233    ExecuteAccessed = 0b1001,
234
235    /// Code Execute/Read
236    ExecuteRead = 0b1010,
237
238    /// Code Execute/Read, accessed
239    ExecuteReadAccessed = 0b1011,
240
241    /// Code Execute-Only, conforming
242    ExecuteConforming = 0b1100,
243
244    /// Code Execute-Only, conforming, accessed
245    ExecuteConformingAccessed = 0b1101,
246
247    /// Code Execute/Read, conforming
248    ExecuteReadConforming = 0b1110,
249
250    /// Code Execute/Read, conforming, accessed
251    ExecuteReadConformingAccessed = 0b1111,
252}
253
254/// Data Segment types for descriptors.
255/// See also Intel 3a, Table 3-1 Code- and Data-Segment Types.
256#[repr(u8)]
257#[derive(Copy, Clone, Debug, Eq, PartialEq)]
258pub enum DataSegmentType {
259    /// Data Read-Only
260    ReadOnly = 0b0000,
261
262    /// Data Read-Only, accessed
263    ReadOnlyAccessed = 0b0001,
264
265    /// Data Read/Write
266    ReadWrite = 0b0010,
267
268    /// Data Read/Write, accessed
269    ReadWriteAccessed = 0b0011,
270
271    /// Data Read-Only, expand-down
272    ReadExpand = 0b0100,
273
274    /// Data Read-Only, expand-down, accessed
275    ReadExpandAccessed = 0b0101,
276
277    /// Data Read/Write, expand-down
278    ReadWriteExpand = 0b0110,
279
280    /// Data Read/Write, expand-down, accessed
281    ReadWriteExpandAccessed = 0b0111,
282}
283
284/// Trait that defines the architecture specific functions for building various system segment descriptors
285/// which are available on all 16, 32, and 64 bits.
286pub trait GateDescriptorBuilder<Size> {
287    fn tss_descriptor(base: u64, limit: u64, available: bool) -> Self;
288    fn call_gate_descriptor(selector: SegmentSelector, offset: Size) -> Self;
289    fn interrupt_descriptor(selector: SegmentSelector, offset: Size) -> Self;
290    fn trap_gate_descriptor(selector: SegmentSelector, offset: Size) -> Self;
291}
292
293impl GateDescriptorBuilder<u32> for DescriptorBuilder {
294    fn tss_descriptor(base: u64, limit: u64, available: bool) -> DescriptorBuilder {
295        let typ = match available {
296            true => DescriptorType::System32(SystemDescriptorTypes32::TssAvailable32),
297            false => DescriptorType::System32(SystemDescriptorTypes32::TssBusy32),
298        };
299        DescriptorBuilder::with_base_limit(base, limit).set_type(typ)
300    }
301
302    fn call_gate_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
303        DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(
304            DescriptorType::System32(SystemDescriptorTypes32::CallGate32),
305        )
306    }
307
308    fn interrupt_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
309        DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(
310            DescriptorType::System32(SystemDescriptorTypes32::InterruptGate32),
311        )
312    }
313
314    fn trap_gate_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
315        DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(
316            DescriptorType::System32(SystemDescriptorTypes32::TrapGate32),
317        )
318    }
319}
320
321/// Trait to define functions that build architecture specific code and data descriptors.
322pub trait SegmentDescriptorBuilder<Size> {
323    fn code_descriptor(base: Size, limit: Size, cst: CodeSegmentType) -> Self;
324    fn data_descriptor(base: Size, limit: Size, dst: DataSegmentType) -> Self;
325}
326
327impl SegmentDescriptorBuilder<u32> for DescriptorBuilder {
328    fn code_descriptor(base: u32, limit: u32, cst: CodeSegmentType) -> DescriptorBuilder {
329        DescriptorBuilder::with_base_limit(base.into(), limit.into())
330            .set_type(DescriptorType::Code(cst))
331    }
332
333    fn data_descriptor(base: u32, limit: u32, dst: DataSegmentType) -> DescriptorBuilder {
334        DescriptorBuilder::with_base_limit(base.into(), limit.into())
335            .set_type(DescriptorType::Data(dst))
336    }
337}
338
339/// Makes building descriptors easier (hopefully).
340pub struct DescriptorBuilder {
341    /// The base defines the location of byte 0 of the segment within the 4-GByte linear address space.
342
343    /// The limit is the size of the range covered by the segment. Really a 20bit value.
344    pub(crate) base_limit: Option<(u64, u64)>,
345
346    /// Alternative to base_limit we use a selector that points to a segment and an an offset for certain descriptors.
347    pub(crate) selector_offset: Option<(SegmentSelector, u64)>,
348
349    /// Descriptor type
350    pub(crate) typ: Option<DescriptorType>,
351
352    /// Specifies the privilege level of the segment. The privilege level can range from 0 to 3, with 0 being the most privileged level.
353    pub(crate) dpl: Option<Ring>,
354
355    /// Indicates whether the segment is present in memory (set) or not present (clear).
356    pub(crate) present: bool,
357
358    /// Available for use by system software
359    pub(crate) avl: bool,
360
361    /// Default operation size
362    pub(crate) db: bool,
363
364    /// Determines the scaling of the segment limit field. When the granularity flag is clear, the segment limit is interpreted in byte units; when flag is set, the segment limit is interpreted in 4-KByte units.
365    pub(crate) limit_granularity_4k: bool,
366
367    /// 64-bit code segment (IA-32e mode only)
368    pub(crate) l: bool,
369}
370
371impl DescriptorBuilder {
372    /// Start building a new descriptor with a base and limit.
373    pub(crate) fn with_base_limit(base: u64, limit: u64) -> DescriptorBuilder {
374        DescriptorBuilder {
375            base_limit: Some((base, limit)),
376            selector_offset: None,
377            typ: None,
378            dpl: None,
379            present: false,
380            avl: false,
381            db: false,
382            limit_granularity_4k: false,
383            l: false,
384        }
385    }
386
387    /// Start building a new descriptor with a segment selector and offset.
388    pub(crate) fn with_selector_offset(
389        selector: SegmentSelector,
390        offset: u64,
391    ) -> DescriptorBuilder {
392        DescriptorBuilder {
393            base_limit: None,
394            selector_offset: Some((selector, offset)),
395            typ: None,
396            dpl: None,
397            present: false,
398            avl: false,
399            db: false,
400            limit_granularity_4k: false,
401            l: false,
402        }
403    }
404
405    /// The segment limit is interpreted in 4-KByte units if this is set.
406    pub fn limit_granularity_4kb(mut self) -> DescriptorBuilder {
407        self.limit_granularity_4k = true;
408        self
409    }
410
411    /// Indicates whether the segment is present in memory (set) or not present (clear).
412    pub fn present(mut self) -> DescriptorBuilder {
413        self.present = true;
414        self
415    }
416
417    /// Specifies the privilege level of the segment.
418    pub fn dpl(mut self, dpl: Ring) -> DescriptorBuilder {
419        self.dpl = Some(dpl);
420        self
421    }
422
423    /// Toggle the AVL bit.
424    pub fn avl(mut self) -> DescriptorBuilder {
425        self.avl = true;
426        self
427    }
428
429    /// Set default operation size (false for 16bit segment, true for 32bit segments).
430    pub fn db(mut self) -> DescriptorBuilder {
431        self.db = true;
432        self
433    }
434
435    pub(crate) fn set_type(mut self, typ: DescriptorType) -> DescriptorBuilder {
436        self.typ = Some(typ);
437        self
438    }
439}
440
441/// Reload stack segment register.
442/// # Safety
443/// Needs CPL 0.
444#[cfg(target_arch = "x86")]
445pub unsafe fn load_ss(sel: SegmentSelector) {
446    unsafe {
447        asm!("movw {0:x}, %ss", in(reg) sel.bits(), options(att_syntax));
448    }
449}
450
451/// Reload data segment register.
452/// # Safety
453/// Needs CPL 0.
454#[cfg(target_arch = "x86")]
455pub unsafe fn load_ds(sel: SegmentSelector) {
456    unsafe {
457        asm!("movw {0:x}, %ds", in(reg) sel.bits(), options(att_syntax));
458    }
459}
460
461/// Reload es segment register.
462/// # Safety
463/// Needs CPL 0.
464#[cfg(target_arch = "x86")]
465pub unsafe fn load_es(sel: SegmentSelector) {
466    unsafe {
467        asm!("movw {0:x}, %es", in(reg) sel.bits(), options(att_syntax));
468    }
469}
470
471/// Reload fs segment register.
472/// # Safety
473/// Needs CPL 0.
474#[cfg(target_arch = "x86")]
475pub unsafe fn load_fs(sel: SegmentSelector) {
476    unsafe {
477        asm!("movw {0:x}, %fs", in(reg) sel.bits(), options(att_syntax));
478    }
479}
480
481/// Reload gs segment register.
482/// # Safety
483/// Needs CPL 0.
484#[cfg(target_arch = "x86")]
485pub unsafe fn load_gs(sel: SegmentSelector) {
486    unsafe {
487        asm!("movw {0:x}, %gs", in(reg) sel.bits(), options(att_syntax));
488    }
489}
490
491#[cfg(target_arch = "x86")]
492pub unsafe fn load_cs(sel: SegmentSelector) {
493    unsafe {
494        asm!("pushl {0}; \
495            pushl $1f; \
496            lretl; \
497            1:", in(reg) sel.bits() as u32, options(att_syntax));
498    }
499}
500
501#[derive(Debug, Eq, PartialEq)]
502pub(crate) enum DescriptorType {
503    System32(SystemDescriptorTypes32),
504    Data(DataSegmentType),
505    Code(CodeSegmentType),
506}
507
508/// System-Segment and Gate-Descriptor Types 32-bit mode.
509
510/// See also Intel 3a, Table 3-2 System Segment and Gate-Descriptor Types.
511
512#[allow(clippy::upper_case_acronyms)]
513#[repr(u8)]
514#[derive(Copy, Clone, Debug, Eq, PartialEq)]
515pub enum SystemDescriptorTypes32 {
516    //Reserved0 = 0b0000,
517    TSSAvailable16 = 0b0001,
518    LDT = 0b0010,
519    TSSBusy16 = 0b0011,
520    CallGate16 = 0b0100,
521    TaskGate = 0b0101,
522    InterruptGate16 = 0b0110,
523    TrapGate16 = 0b0111,
524
525    //Reserved1 = 0b1000,
526    TssAvailable32 = 0b1001,
527
528    //Reserved2 = 0b1010,
529    TssBusy32 = 0b1011,
530    CallGate32 = 0b1100,
531
532    //Reserved3 = 0b1101,
533    InterruptGate32 = 0b1110,
534    TrapGate32 = 0b1111,
535}
536
537//For CI only
538
539#[cfg(not(any(doc, target_arch = "x86")))]
540pub unsafe fn load_ss(_sel: SegmentSelector) {
541    unimplemented!()
542}
543
544#[cfg(not(any(doc, target_arch = "x86")))]
545pub unsafe fn load_ds(_sel: SegmentSelector) {
546    unimplemented!()
547}
548
549#[cfg(not(any(doc, target_arch = "x86")))]
550pub unsafe fn load_es(_sel: SegmentSelector) {
551    unimplemented!()
552}
553
554#[cfg(not(any(doc, target_arch = "x86")))]
555pub unsafe fn load_fs(_sel: SegmentSelector) {
556    unimplemented!()
557}
558
559#[cfg(not(any(doc, target_arch = "x86")))]
560pub unsafe fn load_gs(_sel: SegmentSelector) {
561    unimplemented!()
562}
563
564#[cfg(not(any(doc, target_arch = "x86")))]
565pub unsafe fn load_cs(_sel: SegmentSelector) {
566    unimplemented!()
567}