x86/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 2024.
4
5//! Facilities for managing memory segmentation
6//!
7//! ## Segmentation Model
8//!
9//! The memory model implemented by this module is often referred to as _flat segmentation_, because
10//! the entire address space of 4Gb is contained in a single unbroken ("flat") memory segment. This
11//! technique effectively disables segmentation altogether, allowing the kernel to manage memory
12//! using page tables exclusively.
13//!
14//! To this end, the GDT created by this module contains the following entries:
15//!
16//! * The null segment
17//! * Kernel code segment - DPL of 0. Starts at address zero and extends the full 4Gb.
18//! * Kernel data segment - Same as kernel code segment, but with data segment type.
19//! * Global TSS - Single task state segment used for both kernel and user contexts.
20//! * User code segment - DPL of 3. Starts at address zero and extends the full 4Gb. Memory
21//! protection is done using paging, not segmentation.
22//! * User data segment - Same as user code segment, but with data segment type.
23//!
24//! Segment registers are also populated as follows:
25//!
26//! * CS register -> kernel code segment
27//! * SS/DS/ES/FS/GS registers -> kernel data segment
28//!
29//! ## Task State Segment
30//!
31//! This module initializes a single, global TSS to help with context switching.
32//!
33//! The sole purpose of this TSS is to enable stack switching when transitioning from user to kernel
34//! mode. When an interrupt occurs in user mode, the hardware uses the `ss0` and `esp0` fields of
35//! the TSS to locate the kernel's stack.
36//!
37//! Apart from that, the global TSS is unused. In particular, this crate does _not_ use hardware
38//! task management.
39
40use core::mem;
41use core::ptr;
42
43use crate::registers::bits32::task::TaskStateSegment;
44use crate::registers::dtables::{self, DescriptorTablePointer};
45use crate::registers::ring::Ring;
46use crate::registers::segmentation::{
47 self, BuildDescriptor, CodeSegmentType, DataSegmentType, Descriptor, DescriptorBuilder,
48 GateDescriptorBuilder, SegmentDescriptorBuilder, SegmentSelector,
49};
50use crate::registers::task;
51
52use kernel::static_init;
53
54/// Total number of descriptors in the GDT
55const NUM_DESCRIPTORS: usize = 6;
56
57/// Selector for the kernel's code segment
58pub const KERNEL_CODE: SegmentSelector = SegmentSelector::new(1, Ring::Ring0);
59
60/// Selector for the kernel's data segment
61pub const KERNEL_DATA: SegmentSelector = SegmentSelector::new(2, Ring::Ring0);
62
63/// Selector for the global TSS
64pub const GLOBAL_TSS: SegmentSelector = SegmentSelector::new(3, Ring::Ring0);
65
66/// Selector for the user mode code segment
67pub const USER_CODE: SegmentSelector = SegmentSelector::new(4, Ring::Ring3);
68
69/// Selector for the user mode data segment
70pub const USER_DATA: SegmentSelector = SegmentSelector::new(5, Ring::Ring3);
71
72/// Static storage for a global TSS
73static mut TSS_INSTANCE: TaskStateSegment = TaskStateSegment::new();
74
75/// Initializes the global TSS instance and returns a descriptor for it.
76///
77/// ## Safety
78///
79/// Must never be called more than once, as this would cause the TSS to be reinitialized
80/// unexpectedly.
81unsafe fn init_tss() -> Descriptor {
82 unsafe {
83 TSS_INSTANCE.ss0 = KERNEL_DATA.bits();
84 }
85
86 let tss_base = ptr::addr_of!(TSS_INSTANCE) as u64;
87 let tss_limit = mem::size_of::<TaskStateSegment>() as u64;
88
89 <DescriptorBuilder as GateDescriptorBuilder<u32>>::tss_descriptor(tss_base, tss_limit, true)
90 .dpl(Ring::Ring0)
91 .present()
92 .avl()
93 .finish()
94}
95
96/// Sets the stack pointer to use for handling interrupts from user mode.
97///
98/// ## Safety
99///
100/// When handling interrupts that occur during user mode, the context switching logic has very
101/// specific expectations about the layout of the kernel's stack frame. See _return_from_user.rs_ for
102/// complete details.
103///
104/// When calling this function, the stack frame referenced by `esp` must meet these expectations.
105#[no_mangle]
106pub unsafe extern "cdecl" fn set_tss_esp0(esp: u32) {
107 unsafe {
108 TSS_INSTANCE.esp0 = esp;
109 }
110}
111
112/// Performs global initialization of memory segmentation.
113///
114/// ## Safety
115///
116/// Memory must be identity-mapped before this function is called. Otherwise the kernel's code/data
117/// will suddenly be re-mapped to different addresses, likely resulting in a spectacular crash.
118///
119/// The GDT created by this function is allocated from static program memory. This function must
120/// never be called more than once, or the static GDT will be overwritten.
121pub unsafe fn init() {
122 let gdt = static_init!(
123 [Descriptor; NUM_DESCRIPTORS],
124 [Descriptor::NULL; NUM_DESCRIPTORS]
125 );
126
127 let kernel_code_desc =
128 DescriptorBuilder::code_descriptor(0, u32::MAX, CodeSegmentType::ExecuteRead)
129 .present()
130 .limit_granularity_4kb()
131 .db()
132 .finish();
133 gdt[KERNEL_CODE.index() as usize] = kernel_code_desc;
134
135 let kernel_data_desc =
136 DescriptorBuilder::data_descriptor(0, u32::MAX, DataSegmentType::ReadWrite)
137 .present()
138 .limit_granularity_4kb()
139 .db()
140 .finish();
141 gdt[KERNEL_DATA.index() as usize] = kernel_data_desc;
142
143 let tss_desc = unsafe { init_tss() };
144 gdt[GLOBAL_TSS.index() as usize] = tss_desc;
145
146 let user_code_desc =
147 DescriptorBuilder::code_descriptor(0, u32::MAX, CodeSegmentType::ExecuteRead)
148 .present()
149 .limit_granularity_4kb()
150 .db()
151 .dpl(Ring::Ring3)
152 .finish();
153 gdt[USER_CODE.index() as usize] = user_code_desc;
154
155 let user_data_desc =
156 DescriptorBuilder::data_descriptor(0, u32::MAX, DataSegmentType::ReadWrite)
157 .present()
158 .limit_granularity_4kb()
159 .db()
160 .dpl(Ring::Ring3)
161 .finish();
162 gdt[USER_DATA.index() as usize] = user_data_desc;
163
164 unsafe {
165 dtables::lgdt(&DescriptorTablePointer::new_from_slice(gdt));
166 segmentation::load_cs(KERNEL_CODE);
167 segmentation::load_ss(KERNEL_DATA);
168 segmentation::load_ds(KERNEL_DATA);
169 segmentation::load_es(KERNEL_DATA);
170 segmentation::load_fs(KERNEL_DATA);
171 segmentation::load_gs(KERNEL_DATA);
172 task::load_tr(GLOBAL_TSS);
173 }
174}