Skip to content

Commit 384e27e

Browse files
committed
Create 32-bit GDT and TSS types
1 parent b9fa954 commit 384e27e

File tree

4 files changed

+244
-0
lines changed

4 files changed

+244
-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: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
use bitflags::bitflags;
2+
use bit_field::BitField;
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. Ignored for code segments.
74+
const WRITABLE = 1 << 41;
75+
/// Marks a code segment as “conforming”. This influences the privilege checks that
76+
/// occur on control transfers.
77+
const CONFORMING = 1 << 42;
78+
/// This flag must be set for code segments.
79+
const EXECUTABLE = 1 << 43;
80+
/// This flag must be set for user segments (in contrast to system segments).
81+
const USER_SEGMENT = 1 << 44;
82+
/// Must be set for any segment, causes a segment not present exception if not set.
83+
const PRESENT = 1 << 47;
84+
/// Must be set for long mode code segments.
85+
const LONG_MODE = 1 << 53;
86+
87+
/// The DPL for this descriptor is Ring 3
88+
const DPL_RING_3 = 3 << 45;
89+
}
90+
}
91+
92+
impl Descriptor {
93+
/// Creates a segment descriptor for a long mode kernel code segment.
94+
#[inline]
95+
pub fn kernel_code_segment() -> Descriptor {
96+
use self::DescriptorFlags as Flags;
97+
98+
let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE;
99+
Descriptor(flags.bits())
100+
}
101+
102+
/// Creates a segment descriptor for a long mode ring 3 data segment.
103+
#[inline]
104+
pub fn user_data_segment() -> Descriptor {
105+
use self::DescriptorFlags as Flags;
106+
107+
let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::WRITABLE | Flags::DPL_RING_3;
108+
Descriptor(flags.bits())
109+
}
110+
111+
/// Creates a segment descriptor for a long mode ring 3 code segment.
112+
#[inline]
113+
pub fn user_code_segment() -> Descriptor {
114+
use self::DescriptorFlags as Flags;
115+
116+
let flags = Flags::USER_SEGMENT
117+
| Flags::PRESENT
118+
| Flags::EXECUTABLE
119+
| Flags::DPL_RING_3;
120+
Descriptor(flags.bits())
121+
}
122+
123+
/// Creates a TSS system descriptor for the given TSS.
124+
#[inline]
125+
pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
126+
use self::DescriptorFlags as Flags;
127+
use core::mem::size_of;
128+
129+
let ptr = tss as *const _ as u64;
130+
131+
let mut val = Flags::PRESENT.bits();
132+
// base
133+
val.set_bits(16..40, ptr.get_bits(0..24));
134+
val.set_bits(56..64, ptr.get_bits(24..32));
135+
// limit (the `-1` in needed since the bound is inclusive)
136+
val.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
137+
// type (0b1001 = available 32-bit tss)
138+
val.set_bits(40..44, 0b1001);
139+
140+
Descriptor(val)
141+
}
142+
}
143+
144+
#[derive(Debug, Clone, Copy)]
145+
#[repr(C, packed)]
146+
pub struct TaskStateSegment {
147+
/// Used for hardware task switching
148+
prev_tss: u32,
149+
/// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
150+
pub privilege_stack_table: [Stack; 3],
151+
152+
cr3: u32,
153+
eip: u32,
154+
eflags: u32,
155+
eax: u32,
156+
ecx: u32,
157+
edx: u32,
158+
ebx: u32,
159+
esp: u32,
160+
ebp: u32,
161+
esi: u32,
162+
edi: u32,
163+
es: u32,
164+
cs: u32,
165+
ss: u32,
166+
ds: u32,
167+
fs: u32,
168+
gs: u32,
169+
ldt: u32,
170+
trap: u16,
171+
pub iomap_base: u16,
172+
}
173+
174+
impl TaskStateSegment {
175+
/// Creates a new TSS with zeroed privilege and interrupt stack table and a zero
176+
/// `iomap_base`.
177+
#[inline]
178+
pub const fn new() -> TaskStateSegment {
179+
TaskStateSegment {
180+
privilege_stack_table: [Stack::zero(); 3],
181+
iomap_base: 0,
182+
prev_tss: 0,
183+
cr3: 0,
184+
eip: 0,
185+
eflags: 0,
186+
eax: 0,
187+
ecx: 0,
188+
edx: 0,
189+
ebx: 0,
190+
esp: 0,
191+
ebp: 0,
192+
esi: 0,
193+
edi: 0,
194+
es: 0,
195+
cs: 0,
196+
ss: 0,
197+
ds: 0,
198+
fs: 0,
199+
gs: 0,
200+
ldt: 0,
201+
trap: 0,
202+
}
203+
}
204+
}
205+
206+
#[derive(Debug, Clone, Copy)]
207+
#[repr(C, packed)]
208+
pub struct Stack {
209+
esp: u32,
210+
ss: u32,
211+
}
212+
213+
impl Stack {
214+
fn zero() -> Self {
215+
Stack {
216+
esp: 0,
217+
ss: 0,
218+
}
219+
}
220+
}

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)