Skip to content

Commit 0a5dfa6

Browse files
Merge pull request #110 from rust-osdev/rewrite-gdt
Create 32-bit GDT and TSS types
2 parents b9fa954 + fea81f8 commit 0a5dfa6

File tree

4 files changed

+254
-0
lines changed

4 files changed

+254
-0
lines changed

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/v86/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ authors = ["Ryland Morgan <ryland.hugo@gmail.com>"]
55
edition = "2018"
66

77
[dependencies]
8+
bitflags = "1.2.1"
9+
bit_field = "0.10.0"

src/v86/src/gdt.rs

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
use bit_field::BitField;
2+
use bitflags::bitflags;
3+
4+
#[derive(Debug, Clone)]
5+
pub struct GlobalDescriptorTable {
6+
table: [u64; 8],
7+
next_free: usize,
8+
}
9+
10+
impl GlobalDescriptorTable {
11+
/// Creates an empty GDT.
12+
#[inline]
13+
pub const fn new() -> GlobalDescriptorTable {
14+
GlobalDescriptorTable {
15+
table: [0; 8],
16+
next_free: 1,
17+
}
18+
}
19+
20+
/// Adds the given segment descriptor to the GDT, returning the segment selector.
21+
///
22+
/// Panics if the GDT has no free entries left.
23+
#[inline]
24+
pub fn add_entry(&mut self, entry: Descriptor) -> u16 {
25+
let index = self.push(entry.0);
26+
index as u16
27+
}
28+
29+
/// Loads the GDT in the CPU using the `lgdt` instruction. This does **not** alter any of the
30+
/// segment registers; you **must** (re)load them yourself.
31+
#[inline]
32+
pub fn load(&'static self) {
33+
use core::mem::size_of;
34+
35+
/// A struct describing a pointer to a descriptor table (GDT / IDT).
36+
/// This is in a format suitable for giving to 'lgdt' or 'lidt'.
37+
#[derive(Debug, Clone, Copy)]
38+
#[repr(C, packed)]
39+
struct DescriptorTablePointer {
40+
/// Size of the DT.
41+
pub limit: u16,
42+
/// Pointer to the memory region containing the DT.
43+
pub base: u32,
44+
}
45+
46+
let ptr = DescriptorTablePointer {
47+
base: self.table.as_ptr() as u32,
48+
limit: (self.table.len() * size_of::<u64>() - 1) as u16,
49+
};
50+
51+
llvm_asm!("lgdt ($0)" :: "r" (&ptr) : "memory");
52+
}
53+
54+
#[inline]
55+
fn push(&mut self, value: u64) -> usize {
56+
if self.next_free < self.table.len() {
57+
let index = self.next_free;
58+
self.table[index] = value;
59+
self.next_free += 1;
60+
index
61+
} else {
62+
panic!("GDT full");
63+
}
64+
}
65+
}
66+
67+
#[derive(Debug, Clone)]
68+
pub struct Descriptor(u64);
69+
70+
bitflags! {
71+
/// Flags for a GDT descriptor. Not all flags are valid for all descriptor types.
72+
pub struct DescriptorFlags: u64 {
73+
/// For data segments, this flag sets the segment as writable. For code
74+
/// segments, it defines whether the segment is readable.
75+
const READABLE_WRITABLE = 1 << 41;
76+
/// Marks a code segment as “conforming”. This influences the privilege checks that
77+
/// occur on control transfers.
78+
const CONFORMING = 1 << 42;
79+
/// This flag must be set for code segments.
80+
const EXECUTABLE = 1 << 43;
81+
/// This flag must be set for user segments (in contrast to system segments).
82+
const USER_SEGMENT = 1 << 44;
83+
/// Must be set for any segment, causes a segment not present exception if not set.
84+
const PRESENT = 1 << 47;
85+
/// Must be set for long mode code segments.
86+
const LONG_MODE = 1 << 53;
87+
88+
/// The DPL for this descriptor is Ring 3
89+
const DPL_RING_3 = 3 << 45;
90+
}
91+
}
92+
93+
impl Descriptor {
94+
/// Creates a segment descriptor for a protected mode kernel code segment.
95+
#[inline]
96+
pub fn kernel_code_segment() -> Descriptor {
97+
use self::DescriptorFlags as Flags;
98+
99+
let flags =
100+
Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::READABLE_WRITABLE;
101+
Descriptor(flags.bits())
102+
}
103+
104+
/// Creates a segment descriptor for a protected mode kernel data segment.
105+
#[inline]
106+
pub fn data_segment() -> Descriptor {
107+
use self::DescriptorFlags as Flags;
108+
109+
let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE;
110+
Descriptor(flags.bits())
111+
}
112+
113+
/// Creates a segment descriptor for a protected mode ring 3 data segment.
114+
#[inline]
115+
pub fn user_data_segment() -> Descriptor {
116+
use self::DescriptorFlags as Flags;
117+
118+
let flags =
119+
Flags::USER_SEGMENT | Flags::PRESENT | Flags::READABLE_WRITABLE | Flags::DPL_RING_3;
120+
Descriptor(flags.bits())
121+
}
122+
123+
/// Creates a segment descriptor for a protected mode ring 3 code segment.
124+
#[inline]
125+
pub fn user_code_segment() -> Descriptor {
126+
use self::DescriptorFlags as Flags;
127+
128+
let flags = Flags::USER_SEGMENT
129+
| Flags::PRESENT
130+
| Flags::EXECUTABLE
131+
| Flags::DPL_RING_3
132+
| Flags::READABLE_WRITABLE;
133+
Descriptor(flags.bits())
134+
}
135+
136+
/// Creates a TSS system descriptor for the given TSS.
137+
#[inline]
138+
pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
139+
use self::DescriptorFlags as Flags;
140+
use core::mem::size_of;
141+
142+
let ptr = tss as *const _ as u64;
143+
144+
let mut val = Flags::PRESENT.bits();
145+
// base
146+
val.set_bits(16..40, ptr.get_bits(0..24));
147+
val.set_bits(56..64, ptr.get_bits(24..32));
148+
// limit (the `-1` in needed since the bound is inclusive)
149+
val.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
150+
// type (0b1001 = available 32-bit tss)
151+
val.set_bits(40..44, 0b1001);
152+
153+
Descriptor(val)
154+
}
155+
}
156+
157+
#[derive(Debug, Clone, Copy)]
158+
#[repr(C, packed)]
159+
pub struct TaskStateSegment {
160+
/// Used for hardware task switching
161+
prev_tss: u32,
162+
/// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
163+
pub privilege_stack_table: [Stack; 3],
164+
165+
cr3: u32,
166+
eip: u32,
167+
eflags: u32,
168+
eax: u32,
169+
ecx: u32,
170+
edx: u32,
171+
ebx: u32,
172+
esp: u32,
173+
ebp: u32,
174+
esi: u32,
175+
edi: u32,
176+
es: u32,
177+
cs: u32,
178+
ss: u32,
179+
ds: u32,
180+
fs: u32,
181+
gs: u32,
182+
ldt: u32,
183+
trap: u16,
184+
pub iomap_base: u16,
185+
}
186+
187+
impl TaskStateSegment {
188+
/// Creates a new TSS with zeroed privilege and interrupt stack table and a zero
189+
/// `iomap_base`.
190+
#[inline]
191+
pub const fn new() -> TaskStateSegment {
192+
TaskStateSegment {
193+
privilege_stack_table: [Stack::zero(); 3],
194+
iomap_base: 0,
195+
prev_tss: 0,
196+
cr3: 0,
197+
eip: 0,
198+
eflags: 0,
199+
eax: 0,
200+
ecx: 0,
201+
edx: 0,
202+
ebx: 0,
203+
esp: 0,
204+
ebp: 0,
205+
esi: 0,
206+
edi: 0,
207+
es: 0,
208+
cs: 0,
209+
ss: 0,
210+
ds: 0,
211+
fs: 0,
212+
gs: 0,
213+
ldt: 0,
214+
trap: 0,
215+
}
216+
}
217+
}
218+
219+
#[derive(Debug, Clone, Copy)]
220+
#[repr(C, packed)]
221+
pub struct Stack {
222+
esp: u32,
223+
ss: u32,
224+
}
225+
226+
impl Stack {
227+
fn zero() -> Self {
228+
Stack { esp: 0, ss: 0 }
229+
}
230+
}

src/v86/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![feature(llvm_asm)]
44
#![no_std]
55

6+
pub mod gdt;
67
pub mod idt;
78

89
const EFLAG_IF: u16 = 0x00000200;

0 commit comments

Comments
 (0)