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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! # Memory Segmentation
//!
//! The [GlobalDescriptorTable] and its memory segment [Descriptor]s primarely
//! define continuous memory regions for different purposes.
//! Examples of these memory regions are kernel code or kernel data segments.
//! These segments might overlap and can be defined to span over the whole
//! addressable memory.

use core::arch::asm;
use core::mem::size_of;
use core::ops::RangeInclusive;

use bitfield_struct::bitfield;

use super::paging::Page;
use super::tss::TaskStateSegment;
use super::DescriptorTablePointer;

/// Abstracts the GDT that, primarily, contains descriptors to memory segments.
///
/// The GDT is a table that primarily contains segment descriptors.
/// Segment descriptors has a size of 8 Bytes and contains the size, position,
/// access rights, and purpose of such a segment.
/// Unlike the LDT, the GDT is shared between all processes and may contain TSS and LDT descriptors.
/// For the kernel, the first entry is required to be a null descriptor and the code and data segments.
/// To support user-mode processes, additional TSS, code, and data segments for ring 3 must be added.
///
/// The base address and size of the GDT are written to the GDTR register during boot (via. `lgdt`).
///
/// - ISDMv3, 2.4.1; Global Descriptor Table Register (GDTR)
/// - ISDMv3, 3.5.1; Segment Descriptor Tables
#[derive(Clone, Debug)]
#[repr(C)]
#[repr(align(16))]
pub struct GlobalDescriptorTable<const N: usize>(pub [Descriptor; N]);

impl<const N: usize> GlobalDescriptorTable<N> {
    pub const fn pointer(&self) -> DescriptorTablePointer {
        DescriptorTablePointer {
            limit: (self.0.len() * size_of::<Descriptor>() - 1) as u16,
            base: self.0.as_ptr().cast_mut().cast(),
        }
    }
    /// Load/activate the GDT
    pub fn activate(&'static self) {
        unsafe { asm!("lgdt [{}]", in(reg) &self.pointer()) };
    }

    pub const fn selector(&self, idx: usize, user: bool) -> SegmentSelector {
        assert!(idx < N);

        SegmentSelector::new()
            .with_index(idx as _)
            .with_local(false)
            .with_privilege(if user { Ring::User } else { Ring::System })
    }
}

/// Describes the structure of segment descriptors
///
/// A data structure that contains size, position, access rights, and purpose of any segment.
/// Segment descriptors are used in both the GDT, as well as in LDTs.
///
/// - ISDMv3, 3.4.5; Segment Descriptors
/// - AAPMv2, 4.7 Legacy Segment Descriptors
#[bitfield(u64)]
#[derive(PartialEq, Eq)]
pub struct Descriptor {
    /// Least-significant bits of segment size (influenced by `pages`!)
    #[bits(16)]
    limit_low: u32,
    /// Least-significant bits of base address
    #[bits(24)]
    base_low: u32,
    /// Meaning of those 4 bits depends on `kind_code_data` below
    #[bits(4)]
    pub kind: u8,
    /// Descriptor type (influences the meaning of the 3 bits above)
    pub descriptor_kind: bool,
    /// Ring for this segment
    #[bits(2)]
    pub ring: u8,
    /// Entry is valid iff set to `true`
    pub present: bool,
    /// Most-significant bits of segment size
    #[bits(4)]
    limit_high: u32,
    /// Bit which can be used for other purposes (in software)
    pub available: bool,
    /// Meaning of those 2 bits relate to `descriptor_kind` and `kind`
    #[bits(2)]
    pub custom: u8,
    /// Uses page granularity for the segment limit (instead of bytes)
    pub pages: bool,
    /// most-significant bits of base address
    base_high: u8,
}

impl Descriptor {
    /// Create a code/data segment descriptor
    pub const fn segment(memory: RangeInclusive<u32>, code: bool, user: bool) -> Descriptor {
        Descriptor::new()
            .with_present(true)
            .with_base(*memory.start())
            .with_limit(*memory.end() - *memory.start())
            .with_descriptor_kind(true) // code or data
            .with_kind(if code { 0xA } else { 0x2 }) // code readable / data writeable
            .with_custom(2) // 32 bit
            .with_ring(if user { 3 } else { 0 })
    }

    /// Creates a new task state segment descriptor for the Global Descriptor Table.
    pub fn tss(tss: &TaskStateSegment) -> Descriptor {
        Self::new()
            .with_present(true)
            .with_base(tss as *const _ as _)
            .with_limit((size_of::<TaskStateSegment>() - 1) as _)
            .with_descriptor_kind(false) // system
            .with_kind(0b1001) // sys_type: 80386-TSS
            .with_ring(3)
    }

    /// Base/starting address
    pub const fn base(&self) -> u32 {
        self.base_low() | (self.base_high() as u32) << Self::BASE_LOW_BITS
    }
    /// Base/starting address
    pub const fn with_base(self, v: u32) -> Self {
        self.with_base_low(v & 0x00ff_ffff)
            .with_base_high((v >> Self::BASE_LOW_BITS) as _)
    }
    /// Segment size
    pub const fn limit(&self) -> u32 {
        let shift = if self.pages() { Page::BITS } else { 0 };
        let mask = (1 << shift) - 1;
        ((self.limit_low() | (self.limit_high() << Self::LIMIT_HIGH_BITS)) << shift) | mask
    }
    /// Segment size
    pub const fn with_limit(self, v: u32) -> Self {
        let shift = if v > 0xFFFFF { Page::BITS } else { 0 };
        self.with_limit_low((v >> shift) & 0xFFFF)
            .with_limit_high((v >> Self::LIMIT_LOW_BITS) >> shift)
            .with_pages(shift != 0)
    }
}

/// 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"
#[bitfield(u16)]
#[derive(PartialEq, Eq)]
pub struct SegmentSelector {
    /// Requested privilege level
    #[bits(2)]
    pub privilege: Ring,
    /// Whether this selector corresponds to a global or local descriptor table
    pub local: bool,
    /// Index within the descriptor table
    #[bits(13)]
    pub index: u16,
}

#[repr(u16)]
#[derive(Debug, PartialEq, Eq)]
pub enum Ring {
    System = 0,
    User = 3,
}
impl Ring {
    pub const fn into_bits(self) -> u16 {
        self as _
    }
    pub const fn from_bits(value: u16) -> Self {
        match value {
            0 => Self::System,
            3 => Self::User,
            _ => unreachable!(),
        }
    }
}

/// Returns the current value of the code segment register.
pub fn code_segment() -> SegmentSelector {
    let segment: u16;
    unsafe { asm!("mov {0:x}, cs", out(reg) segment, options(nostack, nomem)) };
    SegmentSelector(segment)
}