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::P2V;
7use bit_field::BitField;
8use core::mem::size_of;
9use core::{arch::asm, slice::from_raw_parts_mut};
10
11// these are 32 bit low-address symbols. we need to promote them to high
12// address mapping.
13extern "C" {
14 fn gdt();
15 // tss_desc is part of the gdt, this tag is only for convenience
16 fn tss_desc();
17 fn gdt_80();
18 // tss0 is already reserved in high memory
19 fn tss0();
20}
21
22/// promote the gdt to high address. unsafe: only call this before dropping low
23/// memory mapping. and only call this once
24pub unsafe fn init() {
25 let gdtd = unsafe { &mut *(gdt_80 as *mut GDTDescriptor) };
26 // sanity check
27 debug_assert!(gdtd.table_size == 7 * 8 - 1);
28 gdtd.table_addr = P2V(gdt as u64).unwrap();
29 unsafe { asm!("lgdt [{}]", in (reg) P2V(gdt_80 as u64).unwrap()) }
30 // set up tss
31 let tssd = from_raw_parts_mut(tss_desc as *mut u64, 2);
32 let (low, high) = to_tss_desc(tss0 as u64);
33 tssd[0] = low;
34 tssd[1] = high;
35 // load tss. Fuck you x86 why this one don't need to minus one?
36 // 0x28 for the 6th entry in gdt.
37 asm!("ltr {0:x}", in(reg) 0x28, options(nostack, preserves_flags));
38}
39
40pub unsafe fn set_tss_ksp(ksp: u64) {
41 let tss = tss0 as *mut TaskStateSegment;
42 (*tss).privilege_stack_table[0] = ksp;
43}
44
45/// gdtd describes the gdt, don't be confused, gdtd is not a gdt entdy (segment
46/// descriptor)
47#[repr(C)]
48#[repr(packed)]
49struct GDTDescriptor {
50 pub table_size: u16,
51 pub table_addr: u64,
52}
53
54// takes the address of the tss segment and returns its descriptor in the
55// gdt(high, low). The low desc contains only the PRESENT bit
56fn to_tss_desc(tss_addr: u64) -> (u64, u64) {
57 // present
58 let mut low: u64 = 1 << 47;
59 // base
60 low.set_bits(16..40, tss_addr.get_bits(0..24));
61 low.set_bits(56..64, tss_addr.get_bits(24..32));
62 // limit (the `-1` in needed since the bound is inclusive)
63 low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
64 // type (0b1001 = available 64-bit tss)
65 low.set_bits(40..44, 0b1001);
66 let mut high: u64 = 0;
67 high.set_bits(0..32, tss_addr.get_bits(32..64));
68 (low, high)
69}
70
71// below are copied from:
72// https://docs.rs/x86_64/0.15.1/src/x86_64/structures/tss.rs.html#12-23
73// TODO: add attributions
74#[derive(Debug, Clone, Copy)]
75#[repr(C, packed(4))]
76pub struct TaskStateSegment {
77 reserved_1: u32,
78 /// The full 64-bit canonical forms of the stack pointers (RSP) for
79 /// privilege levels 0-2.
80 pub privilege_stack_table: [u64; 3],
81 reserved_2: u64,
82 /// The full 64-bit canonical forms of the interrupt stack table (IST)
83 /// pointers.
84 pub interrupt_stack_table: [u64; 7],
85 reserved_3: u64,
86 reserved_4: u16,
87 /// The 16-bit offset to the I/O permission bit map from the 64-bit TSS
88 /// base.
89 pub iomap_base: u16,
90}
91
92impl TaskStateSegment {
93 /// Creates a new TSS with zeroed privilege and interrupt stack table and an
94 /// empty I/O-Permission Bitmap.
95 ///
96 /// As we always set the TSS segment limit to
97 /// `size_of::<TaskStateSegment>() - 1`, this means that `iomap_base` is
98 /// initialized to `size_of::<TaskStateSegment>()`.
99 #[inline]
100 pub const fn new() -> TaskStateSegment {
101 TaskStateSegment {
102 privilege_stack_table: [0; 3],
103 interrupt_stack_table: [0; 7],
104 iomap_base: size_of::<TaskStateSegment>() as u16,
105 reserved_1: 0,
106 reserved_2: 0,
107 reserved_3: 0,
108 reserved_4: 0,
109 }
110 }
111}