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)
}