rustubs/arch/x86_64/
gdt.rs

1//! the x86 gdt struct is so obscure and it's not worth the lines of code to
2//! write proper high level representaion. Also since it only needs to be
3//! written to once or twice, I'll live with the hard coded stuffs in this mod.
4//! You need to suffer all this pain exists because intel/amd doens't want to
5//! ditch segmentation due to backward compatibility. THIS REALLY SUCKS.
6use crate::defs::ExternSyms::{GDT, GDT_80, TSS0, TSS_DESC};
7use crate::defs::P2V;
8use bit_field::BitField;
9use core::mem::size_of;
10use core::{arch::asm, slice::from_raw_parts_mut};
11
12/// promote the gdt to high address. unsafe: only call this before dropping low
13/// memory mapping. and only call this once
14pub unsafe fn init() {
15	let gdtd = unsafe { &mut *(GDT_80 as *mut GDTDescriptor) };
16	// sanity check
17	debug_assert!(gdtd.table_size == 7 * 8 - 1);
18	gdtd.table_addr = P2V(GDT as u64).unwrap();
19	unsafe { asm!("lgdt [{}]", in (reg) P2V(GDT_80 as u64).unwrap()) }
20	// set up tss
21	let tssd = from_raw_parts_mut(TSS_DESC as *mut u64, 2);
22	let (low, high) = to_tss_desc(TSS0 as u64);
23	tssd[0] = low;
24	tssd[1] = high;
25	// load tss. Fuck you x86 why this one don't need to minus one?
26	// 0x28 for the 6th entry in gdt.
27	asm!("ltr {0:x}", in(reg) 0x28, options(nostack, preserves_flags));
28}
29
30pub unsafe fn set_tss_ksp(ksp: u64) {
31	let tss = TSS0 as *mut TaskStateSegment;
32	(*tss).privilege_stack_table[0] = ksp;
33}
34
35/// gdtd describes the  gdt, don't be confused, gdtd is not a gdt entdy (segment
36/// descriptor)
37#[repr(C)]
38#[repr(packed)]
39struct GDTDescriptor {
40	pub table_size: u16,
41	pub table_addr: u64,
42}
43
44// takes the address of the tss segment and returns its descriptor in the
45// gdt(high, low). The low desc contains only the PRESENT bit
46fn to_tss_desc(tss_addr: u64) -> (u64, u64) {
47	// present
48	let mut low: u64 = 1 << 47;
49	// base
50	low.set_bits(16..40, tss_addr.get_bits(0..24));
51	low.set_bits(56..64, tss_addr.get_bits(24..32));
52	// limit (the `-1` in needed since the bound is inclusive)
53	low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
54	// type (0b1001 = available 64-bit tss)
55	low.set_bits(40..44, 0b1001);
56	let mut high: u64 = 0;
57	high.set_bits(0..32, tss_addr.get_bits(32..64));
58	(low, high)
59}
60
61// below are copied from:
62// https://docs.rs/x86_64/0.15.1/src/x86_64/structures/tss.rs.html#12-23
63// TODO: add attributions
64#[derive(Debug, Clone, Copy)]
65#[repr(C, packed(4))]
66pub struct TaskStateSegment {
67	reserved_1: u32,
68	/// The full 64-bit canonical forms of the stack pointers (RSP) for
69	/// privilege levels 0-2.
70	pub privilege_stack_table: [u64; 3],
71	reserved_2: u64,
72	/// The full 64-bit canonical forms of the interrupt stack table (IST)
73	/// pointers.
74	pub interrupt_stack_table: [u64; 7],
75	reserved_3: u64,
76	reserved_4: u16,
77	/// The 16-bit offset to the I/O permission bit map from the 64-bit TSS
78	/// base.
79	pub iomap_base: u16,
80}
81
82impl TaskStateSegment {
83	/// Creates a new TSS with zeroed privilege and interrupt stack table and an
84	/// empty I/O-Permission Bitmap.
85	///
86	/// As we always set the TSS segment limit to
87	/// `size_of::<TaskStateSegment>() - 1`, this means that `iomap_base` is
88	/// initialized to `size_of::<TaskStateSegment>()`.
89	#[inline]
90	pub const fn new() -> TaskStateSegment {
91		TaskStateSegment {
92			privilege_stack_table: [0; 3],
93			interrupt_stack_table: [0; 7],
94			iomap_base: size_of::<TaskStateSegment>() as u16,
95			reserved_1: 0,
96			reserved_2: 0,
97			reserved_3: 0,
98			reserved_4: 0,
99		}
100	}
101}