From 3eca1e3451828b7a5bdcd64b6ac1fe43acfa5c76 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 22 Aug 2016 14:47:33 -0600 Subject: [PATCH] Thread local TSS --- arch/x86_64/src/gdt.rs | 61 ++++++++++++++++++++++++++++++++-------- arch/x86_64/src/start.rs | 7 ++++- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/arch/x86_64/src/gdt.rs b/arch/x86_64/src/gdt.rs index 09a431d..1dc94e8 100644 --- a/arch/x86_64/src/gdt.rs +++ b/arch/x86_64/src/gdt.rs @@ -3,7 +3,7 @@ use core::mem; use x86::dtables::{self, DescriptorTablePointer}; use x86::segmentation::{self, SegmentSelector}; -use x86::task::TaskStateSegment; +use x86::task::{self, TaskStateSegment}; pub const GDT_NULL: usize = 0; pub const GDT_KERNEL_CODE: usize = 1; @@ -33,11 +33,29 @@ 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 mut INIT_GDTR: DescriptorTablePointer = DescriptorTablePointer { + limit: 0, + base: 0 +}; + +static mut INIT_GDT: [GdtEntry; 4] = [ + // Null + GdtEntry::new(0, 0, 0, 0), + // Kernel code + GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), + // Kernel data + GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), + // Kernel TLS + GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE) +]; + +#[thread_local] pub static mut GDTR: DescriptorTablePointer = DescriptorTablePointer { limit: 0, base: 0 }; +#[thread_local] pub static mut GDT: [GdtEntry; 9] = [ // Null GdtEntry::new(0, 0, 0, 0), @@ -53,12 +71,13 @@ pub static mut GDT: [GdtEntry; 9] = [ GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), //TODO: User TLS GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), - //TODO: TSS + // TSS GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_TSS_AVAIL, 0), // TSS must be 16 bytes long, twice the normal size GdtEntry::new(0, 0, 0, 0), ]; +#[thread_local] pub static mut TSS: TaskStateSegment = TaskStateSegment { reserved: 0, rsp: [0; 3], @@ -69,20 +88,24 @@ pub static mut TSS: TaskStateSegment = TaskStateSegment { iomap_base: 0xFFFF }; +/// Initialize GDT on the BSP pub unsafe fn init(tcb_offset: usize) { - GDTR.limit = (GDT.len() * mem::size_of::() - 1) as u16; - GDTR.base = GDT.as_ptr() as u64; + // Setup the initial GDT with TLS, so we can setup the TLS GDT (a little confusing) + // This means that each CPU will have its own GDT, but we only need to define it once as a thread local + INIT_GDTR.limit = (INIT_GDT.len() * mem::size_of::() - 1) as u16; + INIT_GDTR.base = INIT_GDT.as_ptr() as u64; - GDT[GDT_KERNEL_TLS].set_offset(tcb_offset as u32); + // Set the TLS segment to the offset of the Thread Control Block + INIT_GDT[GDT_KERNEL_TLS].set_offset(tcb_offset as u32); - GDT[GDT_TSS].set_offset(&TSS as *const _ as u32); - GDT[GDT_TSS].set_limit(mem::size_of::() as u32); - - init_ap(); + // Run the AP GDT initialization, which does the rest + init_ap(tcb_offset); } -pub unsafe fn init_ap() { - dtables::lgdt(&GDTR); +/// Initialize GDT for an AP +pub unsafe fn init_ap(tcb_offset: usize) { + // Load the initial GDT, before we have access to thread locals + dtables::lgdt(&INIT_GDTR); segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16)); segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16)); @@ -91,7 +114,21 @@ pub unsafe fn init_ap() { segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16)); segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16)); - //TODO: Seperate TSS for each processor task::load_ltr(SegmentSelector::new(GDT_TSS as u16)); + // Now that we have access to thread locals, setup the AP's individual GDT + GDTR.limit = (GDT.len() * mem::size_of::() - 1) as u16; + GDTR.base = GDT.as_ptr() as u64; + + GDT[GDT_KERNEL_TLS].set_offset(tcb_offset as u32); + + // We can now access our TSS, which is a thread local + GDT[GDT_TSS].set_offset(&TSS as *const _ as u32); + GDT[GDT_TSS].set_limit(mem::size_of::() as u32); + + // Load the new GDT, which is correctly located in thread local storage + dtables::lgdt(&GDTR); + + // Load the task register + task::load_ltr(SegmentSelector::new(GDT_TSS as u16)); } #[derive(Copy, Clone, Debug)] diff --git a/arch/x86_64/src/start.rs b/arch/x86_64/src/start.rs index fa12b10..34481ee 100644 --- a/arch/x86_64/src/start.rs +++ b/arch/x86_64/src/start.rs @@ -134,6 +134,11 @@ pub unsafe extern fn kstart() -> ! { /// Entry to rust for an AP pub unsafe extern fn kstart_ap(stack_start: usize, stack_end: usize) -> ! { { + extern { + /// The end of the tbss. + static mut __tbss_end: u8; + } + assert_eq!(BSS_TEST_ZERO, 0); assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); @@ -141,7 +146,7 @@ pub unsafe extern fn kstart_ap(stack_start: usize, stack_end: usize) -> ! { let mut active_table = paging::init(stack_start, stack_end); // Set up GDT for AP - gdt::init_ap(); + gdt::init_ap((&__tbss_end as *const u8 as *const usize).offset(-1) as usize); // Set up IDT for AP idt::init();