282 lines
7.3 KiB
Rust
282 lines
7.3 KiB
Rust
#[allow(dead_code)]
|
|
#[repr(C, packed)]
|
|
pub struct Entry {
|
|
pub limit_lo: u16,
|
|
pub base_lo: u16,
|
|
pub base_mi: u8,
|
|
pub access: u8,
|
|
pub flags: u8,
|
|
pub base_hi: u8,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[repr(C, packed)]
|
|
pub struct Pointer {
|
|
pub limit: u16,
|
|
pub offset: usize,
|
|
}
|
|
|
|
impl Entry {
|
|
const ACC_PRESENT: u8 = 1 << 7;
|
|
const ACC_SYSTEM: u8 = 1 << 4;
|
|
const ACC_EXECUTE: u8 = 1 << 3;
|
|
const ACC_WRITE: u8 = 1 << 1;
|
|
|
|
#[allow(unused)]
|
|
const ACC_RING3: u8 = 3 << 5;
|
|
#[allow(unused)]
|
|
const ACC_ACCESS: u8 = 1 << 0;
|
|
|
|
pub const NULL: Self = Self {
|
|
base_lo: 0,
|
|
base_mi: 0,
|
|
base_hi: 0,
|
|
access: 0,
|
|
flags: 0,
|
|
limit_lo: 0,
|
|
};
|
|
|
|
const fn new(base: u32, limit: u32, flags: u8, access: u8) -> Self {
|
|
Self {
|
|
base_lo: (base & 0xFFFF) as u16,
|
|
base_mi: ((base >> 16) & 0xFF) as u8,
|
|
base_hi: ((base >> 24) & 0xFF) as u8,
|
|
access,
|
|
flags: (flags & 0xF0) | (((limit >> 16) & 0xF) as u8),
|
|
limit_lo: (limit & 0xFFFF) as u16,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
|
mod imp {
|
|
use super::Entry;
|
|
|
|
impl Entry {
|
|
pub const FLAG_4K: u8 = 1 << 7;
|
|
pub const FLAG_32: u8 = 1 << 6;
|
|
|
|
pub const RING0_CS32: Entry = Entry::new(
|
|
0,
|
|
0xFFFFF,
|
|
Entry::FLAG_4K | Entry::FLAG_32,
|
|
Entry::ACC_ACCESS | Entry::ACC_SYSTEM | Entry::ACC_EXECUTE | Entry::ACC_PRESENT,
|
|
);
|
|
pub const RING0_DS32: Entry = Entry::new(
|
|
0,
|
|
0xFFFFF,
|
|
Entry::FLAG_4K | Entry::FLAG_32,
|
|
Entry::ACC_ACCESS | Entry::ACC_SYSTEM | Entry::ACC_WRITE | Entry::ACC_PRESENT,
|
|
);
|
|
pub const RING3_CS32: Entry = Entry::new(
|
|
0,
|
|
0xFFFFF,
|
|
Entry::FLAG_4K | Entry::FLAG_32,
|
|
Entry::ACC_SYSTEM | Entry::ACC_EXECUTE | Entry::ACC_PRESENT | Entry::ACC_RING3,
|
|
);
|
|
pub const RING3_DS32: Entry = Entry::new(
|
|
0,
|
|
0xFFFFF,
|
|
Entry::FLAG_4K | Entry::FLAG_32,
|
|
Entry::ACC_SYSTEM | Entry::ACC_WRITE | Entry::ACC_PRESENT | Entry::ACC_RING3,
|
|
);
|
|
pub const RING3_GS32: Entry = Entry::new(
|
|
0,
|
|
0xF,
|
|
Entry::FLAG_32,
|
|
Entry::ACC_SYSTEM | Entry::ACC_WRITE | Entry::ACC_PRESENT | Entry::ACC_RING3,
|
|
);
|
|
|
|
pub const fn tss(base: u32, limit: u32) -> Self {
|
|
Self::new(
|
|
base,
|
|
limit,
|
|
0,
|
|
Entry::ACC_ACCESS | Entry::ACC_PRESENT | Entry::ACC_EXECUTE,
|
|
)
|
|
}
|
|
|
|
/// Updates the base of this descriptor.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The caller must ensure this code cannot be preempted and the segment will be properly
|
|
/// reloaded afterwards to avoid desync between the GDT and the segment shadow.
|
|
pub unsafe fn set_base(&mut self, value: usize) {
|
|
use core::sync::atomic::Ordering;
|
|
|
|
self.base_lo = (value & 0xFFFF) as u16;
|
|
self.base_mi = ((value >> 16) & 0xFF) as u8;
|
|
self.base_hi = ((value >> 24) & 0xFF) as u8;
|
|
|
|
core::sync::atomic::fence(Ordering::Release);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
|
mod imp {
|
|
use alloc::boxed::Box;
|
|
|
|
use super::{Entry, Pointer};
|
|
|
|
#[allow(dead_code)]
|
|
#[repr(C, packed)]
|
|
pub struct Tss {
|
|
_0: u32,
|
|
rsp0: u64,
|
|
rsp1: u64,
|
|
rsp2: u64,
|
|
_1: u32,
|
|
ist1: u64,
|
|
ist2: u64,
|
|
ist3: u64,
|
|
ist4: u64,
|
|
ist5: u64,
|
|
ist6: u64,
|
|
ist7: u64,
|
|
_2: u64,
|
|
_3: u16,
|
|
iopb_base: u16,
|
|
}
|
|
|
|
impl Tss {
|
|
const NULL: Self = Self {
|
|
_0: 0,
|
|
rsp0: 0,
|
|
rsp1: 0,
|
|
rsp2: 0,
|
|
_1: 0,
|
|
ist1: 0,
|
|
ist2: 0,
|
|
ist3: 0,
|
|
ist4: 0,
|
|
ist5: 0,
|
|
ist6: 0,
|
|
ist7: 0,
|
|
_2: 0,
|
|
_3: 0,
|
|
iopb_base: size_of::<Tss>() as u16,
|
|
};
|
|
}
|
|
|
|
impl Entry {
|
|
const FLAG_LONG: u8 = 1 << 5;
|
|
|
|
const RING0_CS64: Self = Entry::new(
|
|
0,
|
|
0,
|
|
Entry::FLAG_LONG,
|
|
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_EXECUTE,
|
|
);
|
|
const RING0_DS64: Self = Entry::new(
|
|
0,
|
|
0,
|
|
0,
|
|
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_WRITE,
|
|
);
|
|
const RING3_DS64: Self = Entry::new(
|
|
0,
|
|
0,
|
|
0,
|
|
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_RING3 | Entry::ACC_WRITE,
|
|
);
|
|
const RING3_CS64: Self = Entry::new(
|
|
0,
|
|
0,
|
|
Entry::FLAG_LONG,
|
|
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_RING3 | Entry::ACC_EXECUTE,
|
|
);
|
|
|
|
const fn tss_low(base: u32, limit: u32) -> Self {
|
|
Self::new(
|
|
base,
|
|
limit,
|
|
Entry::FLAG_LONG,
|
|
Entry::ACC_ACCESS | Entry::ACC_PRESENT | Entry::ACC_EXECUTE,
|
|
)
|
|
}
|
|
}
|
|
|
|
pub fn create_gdt() -> (&'static [Entry], &'static Tss) {
|
|
// Won't be deallocated, so leaks are not a concern
|
|
let tss = Box::leak(Box::new(Tss::NULL));
|
|
let tss_addr = (tss as *mut Tss).addr();
|
|
let mut gdt = Box::new([
|
|
Entry::NULL,
|
|
Entry::RING0_CS64,
|
|
Entry::RING0_DS64,
|
|
Entry::RING3_DS64,
|
|
Entry::RING3_CS64,
|
|
Entry::tss_low(tss_addr as u32, (size_of::<Tss>() - 1) as u32),
|
|
Entry::NULL,
|
|
]);
|
|
|
|
let tss_high = &mut gdt[6] as *mut _ as *mut u64;
|
|
unsafe { tss_high.write_unaligned((tss_addr >> 32) as u64) };
|
|
|
|
(Box::leak(gdt), tss)
|
|
}
|
|
|
|
pub(super) unsafe fn load_gdt(gdt: &[Entry]) {
|
|
let gdt_addr = gdt.as_ptr().addr();
|
|
let gdtr = Pointer {
|
|
limit: size_of_val(gdt) as u16 - 1,
|
|
offset: gdt_addr,
|
|
};
|
|
|
|
unsafe {
|
|
core::arch::asm!(
|
|
r#"
|
|
wbinvd
|
|
lgdt ({0})
|
|
|
|
// Have to use iretq here
|
|
mov %rsp, %rcx
|
|
leaq 1f(%rip), %rax
|
|
|
|
// SS:RSP
|
|
pushq $0x10
|
|
pushq %rcx
|
|
|
|
// RFLAGS
|
|
pushfq
|
|
|
|
// CS:RIP
|
|
pushq $0x08
|
|
pushq %rax
|
|
iretq
|
|
1:
|
|
mov $0x10, %ax
|
|
mov %ax, %ds
|
|
mov %ax, %es
|
|
mov %ax, %fs
|
|
mov %ax, %gs
|
|
mov %ax, %ss
|
|
|
|
mov $0x28, %ax
|
|
ltr %ax
|
|
"#,
|
|
in(reg) &gdtr,
|
|
out("rax") _,
|
|
out("rcx") _,
|
|
options(att_syntax)
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Initializes and loads the GDT data structure for the current CPU.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Intended to be called once per each CPU during their early initialization.
|
|
pub unsafe fn init() -> usize {
|
|
let (gdt, tss) = create_gdt();
|
|
unsafe { load_gdt(gdt) };
|
|
(tss as *const Tss).addr()
|
|
}
|
|
}
|
|
|
|
#[cfg(not(target_arch = "x86"))]
|
|
pub use imp::*;
|