270 lines
8.6 KiB
Rust
270 lines
8.6 KiB
Rust
//! Global descriptor table
|
|
|
|
use core::ptr::addr_of_mut;
|
|
use core::{convert::TryInto, mem, cell::{Cell, RefCell}};
|
|
use core::sync::atomic::AtomicBool;
|
|
|
|
use crate::cpu_set::LogicalCpuId;
|
|
|
|
use x86::{
|
|
bits32::task::TaskStateSegment,
|
|
dtables::{self, DescriptorTablePointer},
|
|
segmentation::{self, Descriptor as SegmentDescriptor, SegmentSelector},
|
|
task, Ring,
|
|
};
|
|
|
|
use crate::paging::{RmmA, RmmArch, PAGE_SIZE};
|
|
|
|
use super::cpuid::cpuid;
|
|
|
|
pub const GDT_NULL: usize = 0;
|
|
pub const GDT_KERNEL_CODE: usize = 1;
|
|
pub const GDT_KERNEL_DATA: usize = 2;
|
|
pub const GDT_KERNEL_PERCPU: usize = 3;
|
|
pub const GDT_USER_CODE: usize = 4;
|
|
pub const GDT_USER_DATA: usize = 5;
|
|
pub const GDT_USER_FS: usize = 6;
|
|
pub const GDT_USER_GS: usize = 7;
|
|
pub const GDT_TSS: usize = 8;
|
|
|
|
pub const GDT_A_PRESENT: u8 = 1 << 7;
|
|
pub const GDT_A_RING_0: u8 = 0 << 5;
|
|
pub const GDT_A_RING_1: u8 = 1 << 5;
|
|
pub const GDT_A_RING_2: u8 = 2 << 5;
|
|
pub const GDT_A_RING_3: u8 = 3 << 5;
|
|
pub const GDT_A_SYSTEM: u8 = 1 << 4;
|
|
pub const GDT_A_EXECUTABLE: u8 = 1 << 3;
|
|
pub const GDT_A_CONFORMING: u8 = 1 << 2;
|
|
pub const GDT_A_PRIVILEGE: u8 = 1 << 1;
|
|
pub const GDT_A_DIRTY: u8 = 1;
|
|
|
|
pub const GDT_A_TSS_AVAIL: u8 = 0x9;
|
|
pub const GDT_A_TSS_BUSY: u8 = 0xB;
|
|
|
|
pub const GDT_F_PAGE_SIZE: u8 = 1 << 7;
|
|
pub const GDT_F_PROTECTED_MODE: u8 = 1 << 6;
|
|
pub const GDT_F_LONG_MODE: u8 = 1 << 5;
|
|
|
|
static INIT_GDT: [GdtEntry; 3] = [
|
|
// Null
|
|
GdtEntry::new(0, 0, 0, 0),
|
|
// Kernel code
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
// Kernel data
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
];
|
|
|
|
const BASE_GDT: [GdtEntry; 9] = [
|
|
// Null
|
|
GdtEntry::new(0, 0, 0, 0),
|
|
// Kernel code
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
// Kernel data
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
// Kernel TLS
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
// User (32-bit) code
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
// User data
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
// User FS (for TLS)
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
// User GS (for TLS)
|
|
GdtEntry::new(
|
|
0,
|
|
0xFFFFF,
|
|
GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE,
|
|
GDT_F_PAGE_SIZE | GDT_F_PROTECTED_MODE,
|
|
),
|
|
// TSS
|
|
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_TSS_AVAIL, 0),
|
|
];
|
|
|
|
#[repr(C, align(4096))]
|
|
pub struct ProcessorControlRegion {
|
|
pub self_ref: usize,
|
|
pub user_rsp_tmp: usize,
|
|
pub tss: TssWrapper,
|
|
pub gdt: [GdtEntry; 9],
|
|
percpu: crate::percpu::PercpuBlock,
|
|
}
|
|
|
|
// NOTE: Despite not using #[repr(packed)], we do know that while there may be some padding
|
|
// inserted before and after the TSS, the main TSS structure will remain intact.
|
|
#[repr(C, align(16))]
|
|
pub struct TssWrapper(pub TaskStateSegment);
|
|
|
|
pub unsafe fn pcr() -> *mut ProcessorControlRegion {
|
|
let mut ret: *mut ProcessorControlRegion;
|
|
core::arch::asm!("mov {}, gs:[{}]", out(reg) ret, const(core::mem::offset_of!(ProcessorControlRegion, self_ref)));
|
|
ret
|
|
}
|
|
|
|
#[cfg(feature = "pti")]
|
|
pub unsafe fn set_tss_stack(stack: usize) {
|
|
use super::pti::{PTI_CONTEXT_STACK, PTI_CPU_STACK};
|
|
addr_of_mut!((*pcr()).tss.0.ss0).write((GDT_KERNEL_DATA << 3) as u16);
|
|
addr_of_mut!((*pcr()).tss.0.esp0)
|
|
.write((PTI_CPU_STACK.as_ptr() as usize + PTI_CPU_STACK.len()) as u32);
|
|
PTI_CONTEXT_STACK = stack;
|
|
}
|
|
|
|
#[cfg(not(feature = "pti"))]
|
|
pub unsafe fn set_tss_stack(stack: usize) {
|
|
addr_of_mut!((*pcr()).tss.0.ss0).write((GDT_KERNEL_DATA << 3) as u16);
|
|
addr_of_mut!((*pcr()).tss.0.esp0).write(stack as u32);
|
|
}
|
|
|
|
/// Initialize a minimal GDT without configuring percpu.
|
|
pub unsafe fn init() {
|
|
// Load the initial GDT, before the kernel remaps itself.
|
|
dtables::lgdt(&DescriptorTablePointer {
|
|
limit: (INIT_GDT.len() * mem::size_of::<GdtEntry>() - 1) as u16,
|
|
base: INIT_GDT.as_ptr() as *const SegmentDescriptor,
|
|
});
|
|
|
|
// Load the segment descriptors
|
|
segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16, Ring::Ring0));
|
|
segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
segmentation::load_es(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
}
|
|
|
|
/// Initialize GDT and configure percpu.
|
|
pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) {
|
|
let pcr_frame = crate::memory::allocate_frames(1).expect("failed to allocate PCR frame");
|
|
let pcr =
|
|
&mut *(RmmA::phys_to_virt(pcr_frame.start_address()).data() as *mut ProcessorControlRegion);
|
|
|
|
pcr.self_ref = pcr as *const _ as usize;
|
|
pcr.gdt = BASE_GDT;
|
|
pcr.gdt[GDT_KERNEL_PERCPU].set_offset(pcr as *const _ as u32);
|
|
|
|
let gdtr: DescriptorTablePointer<SegmentDescriptor> = DescriptorTablePointer {
|
|
limit: (pcr.gdt.len() * mem::size_of::<GdtEntry>() - 1) as u16,
|
|
base: pcr.gdt.as_ptr() as *const SegmentDescriptor,
|
|
};
|
|
|
|
{
|
|
let tss = &pcr.tss.0 as *const _ as usize as u32;
|
|
|
|
pcr.gdt[GDT_TSS].set_offset(tss);
|
|
pcr.gdt[GDT_TSS].set_limit(mem::size_of::<TaskStateSegment>() as u32);
|
|
}
|
|
|
|
// Load the new GDT, which is correctly located in thread local storage.
|
|
dtables::lgdt(&gdtr);
|
|
|
|
// Reload the segment descriptors
|
|
segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16, Ring::Ring0));
|
|
segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
segmentation::load_es(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
|
|
// TODO: Use FS for kernel TLS on i686?
|
|
segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0));
|
|
segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_PERCPU as u16, Ring::Ring0));
|
|
|
|
// Set the stack pointer to use when coming back from userspace.
|
|
set_tss_stack(stack_offset);
|
|
|
|
// Load the task register
|
|
task::load_tr(SegmentSelector::new(GDT_TSS as u16, Ring::Ring0));
|
|
|
|
pcr.percpu = crate::percpu::PercpuBlock {
|
|
cpu_id,
|
|
switch_internals: Default::default(),
|
|
current_addrsp: RefCell::new(None),
|
|
new_addrsp_tmp: Cell::new(None),
|
|
wants_tlb_shootdown: AtomicBool::new(false),
|
|
};
|
|
crate::percpu::init_tlb_shootdown(cpu_id, &mut pcr.percpu);
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
#[repr(packed)]
|
|
pub struct GdtEntry {
|
|
pub limitl: u16,
|
|
pub offsetl: u16,
|
|
pub offsetm: u8,
|
|
pub access: u8,
|
|
pub flags_limith: u8,
|
|
pub offseth: u8,
|
|
}
|
|
|
|
impl GdtEntry {
|
|
pub const fn new(offset: u32, limit: u32, access: u8, flags: u8) -> Self {
|
|
GdtEntry {
|
|
limitl: limit as u16,
|
|
offsetl: offset as u16,
|
|
offsetm: (offset >> 16) as u8,
|
|
access,
|
|
flags_limith: flags & 0xF0 | ((limit >> 16) as u8) & 0x0F,
|
|
offseth: (offset >> 24) as u8,
|
|
}
|
|
}
|
|
|
|
pub fn offset(&self) -> u32 {
|
|
(self.offsetl as u32) | ((self.offsetm as u32) << 16) | ((self.offseth as u32) << 24)
|
|
}
|
|
|
|
pub fn set_offset(&mut self, offset: u32) {
|
|
self.offsetl = offset as u16;
|
|
self.offsetm = (offset >> 16) as u8;
|
|
self.offseth = (offset >> 24) as u8;
|
|
}
|
|
|
|
pub fn set_limit(&mut self, limit: u32) {
|
|
self.limitl = limit as u16;
|
|
self.flags_limith = self.flags_limith & 0xF0 | ((limit >> 16) as u8) & 0x0F;
|
|
}
|
|
}
|
|
|
|
impl crate::percpu::PercpuBlock {
|
|
pub fn current() -> &'static Self {
|
|
unsafe { &*core::ptr::addr_of!((*pcr()).percpu) }
|
|
}
|
|
}
|