From 8ddd0ad3f051b75db6340913b01bf1b5264119a4 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 19 Aug 2016 17:38:37 -0600 Subject: [PATCH] WIP: Thread control block and TSS in kernel --- arch/x86_64/src/gdt.rs | 43 ++++++++++++----- arch/x86_64/src/lib.rs | 9 +++- arch/x86_64/src/paging/mapper.rs | 3 +- arch/x86_64/src/paging/mod.rs | 80 +++++++++++++++++++++----------- arch/x86_64/src/start.rs | 48 ++++++++++++++++--- arch/x86_64/src/tcb.rs | 5 ++ bootloader/x86/kernel.ld | 26 +++++++++-- x86_64-unknown-none.json | 2 +- 8 files changed, 161 insertions(+), 55 deletions(-) create mode 100644 arch/x86_64/src/tcb.rs diff --git a/arch/x86_64/src/gdt.rs b/arch/x86_64/src/gdt.rs index 6780f20..09a431d 100644 --- a/arch/x86_64/src/gdt.rs +++ b/arch/x86_64/src/gdt.rs @@ -3,16 +3,17 @@ use core::mem; use x86::dtables::{self, DescriptorTablePointer}; use x86::segmentation::{self, SegmentSelector}; -use x86::task::{self, TaskStateSegment}; +use x86::task::TaskStateSegment; pub const GDT_NULL: usize = 0; pub const GDT_KERNEL_CODE: usize = 1; pub const GDT_KERNEL_DATA: usize = 2; -pub const GDT_USER_CODE: usize = 3; -pub const GDT_USER_DATA: usize = 4; -pub const GDT_USER_TLS: usize = 5; -pub const GDT_TSS: usize = 6; -pub const GDT_TSS_HIGH: usize = 7; +pub const GDT_KERNEL_TLS: usize = 3; +pub const GDT_USER_CODE: usize = 4; +pub const GDT_USER_DATA: usize = 5; +pub const GDT_USER_TLS: usize = 6; +pub const GDT_TSS: usize = 7; +pub const GDT_TSS_HIGH: usize = 8; pub const GDT_A_PRESENT: u8 = 1 << 7; pub const GDT_A_RING_0: u8 = 0 << 5; @@ -37,13 +38,15 @@ pub static mut GDTR: DescriptorTablePointer = DescriptorTablePointer { base: 0 }; -pub static mut GDT: [GdtEntry; 8] = [ +pub static mut GDT: [GdtEntry; 9] = [ // 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), // User code GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), // User data @@ -51,7 +54,7 @@ pub static mut GDT: [GdtEntry; 8] = [ //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 - GdtEntry::new(0, 0, 0 , 0), + 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), ]; @@ -66,18 +69,25 @@ pub static mut TSS: TaskStateSegment = TaskStateSegment { iomap_base: 0xFFFF }; -pub unsafe fn init() { +pub unsafe fn init(tcb_offset: usize) { GDTR.limit = (GDT.len() * mem::size_of::() - 1) as u16; GDTR.base = GDT.as_ptr() as u64; - GDT[GDT_TSS] = GdtEntry::new(&TSS as *const _ as u32, mem::size_of::() as u32, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_TSS_AVAIL, 0); + 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(); +} + +pub unsafe fn init_ap() { dtables::lgdt(&GDTR); segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16)); segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16)); segmentation::load_es(SegmentSelector::new(GDT_KERNEL_DATA as u16)); - segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_DATA as u16)); + segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_TLS as u16)); segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16)); segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16)); @@ -106,4 +116,15 @@ impl GdtEntry { offseth: (offset >> 24) as u8 } } + + 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; + } } diff --git a/arch/x86_64/src/lib.rs b/arch/x86_64/src/lib.rs index 920c809..5d2eea5 100644 --- a/arch/x86_64/src/lib.rs +++ b/arch/x86_64/src/lib.rs @@ -44,7 +44,8 @@ macro_rules! interrupt { } // Push scratch registers - asm!("push rax + asm!("xchg bx, bx + push rax push rcx push rdx push rdi @@ -85,7 +86,8 @@ macro_rules! interrupt_error { } // Push scratch registers - asm!("push rax + asm!("xchg bx, bx + push rax push rcx push rdx push rdi @@ -148,3 +150,6 @@ pub mod serial; /// Initialization and start function pub mod start; + +/// Thread control block +pub mod tcb; diff --git a/arch/x86_64/src/paging/mapper.rs b/arch/x86_64/src/paging/mapper.rs index 471362a..5ca7570 100644 --- a/arch/x86_64/src/paging/mapper.rs +++ b/arch/x86_64/src/paging/mapper.rs @@ -33,7 +33,8 @@ impl Mapper { let mut p1 = p2.next_table_create(page.p2_index()); assert!(p1[page.p1_index()].is_unused(), - "Set to {:X}: {:?}, requesting {:X}: {:?}", + "{:X}: Set to {:X}: {:?}, requesting {:X}: {:?}", + page.start_address().get(), p1[page.p1_index()].address().get(), p1[page.p1_index()].flags(), frame.start_address().get(), flags); p1[page.p1_index()].set(frame, flags | entry::PRESENT); diff --git a/arch/x86_64/src/paging/mod.rs b/arch/x86_64/src/paging/mod.rs index 550c205..bad0649 100644 --- a/arch/x86_64/src/paging/mod.rs +++ b/arch/x86_64/src/paging/mod.rs @@ -2,8 +2,10 @@ //! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/modifying-page-tables.html) use core::ops::{Deref, DerefMut}; +use x86::tlb; use memory::{allocate_frame, Frame}; +use tcb::ThreadControlBlock; use self::entry::{EntryFlags, PRESENT, WRITABLE, NO_EXECUTE}; use self::mapper::Mapper; @@ -43,6 +45,8 @@ pub unsafe fn init(stack_start: usize, stack_end: usize) -> ActivePageTable { static mut __tbss_start: u8; /// The ending byte of the thread BSS segment static mut __tbss_end: u8; + /// The start of the thread control block + static mut __tcb: ThreadControlBlock; /// The starting byte of the _.bss_ (uninitialized data) segment. static mut __bss_start: u8; /// The ending byte of the _.bss_ (uninitialized data) segment. @@ -61,10 +65,12 @@ pub unsafe fn init(stack_start: usize, stack_end: usize) -> ActivePageTable { active_table.with(&mut new_table, &mut temporary_page, |mapper| { { let mut remap = |start: usize, end: usize, flags: EntryFlags| { - let start_frame = Frame::containing_address(PhysicalAddress::new(start)); - let end_frame = Frame::containing_address(PhysicalAddress::new(end - 1)); - for frame in Frame::range_inclusive(start_frame, end_frame) { - mapper.identity_map(frame, flags); + if end > start { + let start_frame = Frame::containing_address(PhysicalAddress::new(start)); + let end_frame = Frame::containing_address(PhysicalAddress::new(end - 1)); + for frame in Frame::range_inclusive(start_frame, end_frame) { + mapper.identity_map(frame, flags); + } } }; @@ -90,42 +96,60 @@ pub unsafe fn init(stack_start: usize, stack_end: usize) -> ActivePageTable { // Map and copy TDATA { - temporary_page.map(allocate_frame().expect("no more frames"), PRESENT | NO_EXECUTE | WRITABLE, &mut active_table); + let start = & __tdata_start as *const _ as usize; + let end = & __tdata_end as *const _ as usize; + if end > start { + temporary_page.map(allocate_frame().expect("no more frames"), PRESENT | NO_EXECUTE | WRITABLE, &mut active_table); - let start = & __tbss_start as *const _ as usize; - let end = & __tbss_end as *const _ as usize; - let start_page = Page::containing_address(VirtualAddress::new(start)); - let end_page = Page::containing_address(VirtualAddress::new(end - 1)); - for page in Page::range_inclusive(start_page, end_page) { - // Copy master to temporary page - { - let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get())); - active_table.identity_map(frame, PRESENT | NO_EXECUTE); - ::externs::memcpy(temporary_page.start_address().get() as *mut u8, page.start_address().get() as *const u8, 4096); - active_table.unmap(page); - } - // Copy temporary page to CPU copy - { - active_table.map(page, PRESENT | NO_EXECUTE | WRITABLE); - ::externs::memcpy(page.start_address().get() as *mut u8, temporary_page.start_address().get() as *const u8, 4096); + let start_page = Page::containing_address(VirtualAddress::new(start)); + let end_page = Page::containing_address(VirtualAddress::new(end - 1)); + for page in Page::range_inclusive(start_page, end_page) { + // Copy parent to temporary page + { + let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get())); + active_table.identity_map(frame, PRESENT | NO_EXECUTE); + tlb::flush_all(); + ::externs::memcpy(temporary_page.start_address().get() as *mut u8, page.start_address().get() as *const u8, 4096); + active_table.unmap(page); + } + // Copy temporary page to child + { + active_table.map(page, PRESENT | NO_EXECUTE | WRITABLE); + tlb::flush_all(); + ::externs::memcpy(page.start_address().get() as *mut u8, temporary_page.start_address().get() as *const u8, 4096); + } } + + temporary_page.unmap(&mut active_table); } - - temporary_page.unmap(&mut active_table); } // Map and clear TBSS { let start = & __tbss_start as *const _ as usize; let end = & __tbss_end as *const _ as usize; - let start_page = Page::containing_address(VirtualAddress::new(start)); - let end_page = Page::containing_address(VirtualAddress::new(end - 1)); - for page in Page::range_inclusive(start_page, end_page) { - active_table.map(page, PRESENT | NO_EXECUTE | WRITABLE); - ::externs::memset(page.start_address().get() as *mut u8, 0, 4096); + if end > start { + let start_page = Page::containing_address(VirtualAddress::new(start)); + let end_page = Page::containing_address(VirtualAddress::new(end - 1)); + for page in Page::range_inclusive(start_page, end_page) { + active_table.map(page, PRESENT | NO_EXECUTE | WRITABLE); + tlb::flush_all(); + ::externs::memset(page.start_address().get() as *mut u8, 0, 4096); + } } } + // Map and set TCB + { + let start = & __tcb as *const _ as usize; + println!("TCB: {:X}", start); + let page = Page::containing_address(VirtualAddress::new(start)); + active_table.map(page, PRESENT | NO_EXECUTE | WRITABLE); + tlb::flush_all(); + ::externs::memset(page.start_address().get() as *mut u8, 0, 4096); + __tcb.offset = start; + } + active_table } diff --git a/arch/x86_64/src/start.rs b/arch/x86_64/src/start.rs index 04feccd..0265ed5 100644 --- a/arch/x86_64/src/start.rs +++ b/arch/x86_64/src/start.rs @@ -12,11 +12,18 @@ use gdt; use idt; use memory::{self, Frame}; use paging::{self, entry, Page, PhysicalAddress, VirtualAddress}; +use tcb::ThreadControlBlock; /// Test of zero values in BSS. static BSS_TEST_ZERO: usize = 0; -/// Test of non-zero values in BSS. -static BSS_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF; +/// Test of non-zero values in data. +static DATA_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF; +/// Test of zero values in thread BSS +#[thread_local] +static mut TBSS_TEST_ZERO: usize = 0; +/// Test of non-zero values in thread data. +#[thread_local] +static mut TDATA_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF; static AP_COUNT: AtomicUsize = ATOMIC_USIZE_INIT; static BSP_READY: AtomicBool = ATOMIC_BOOL_INIT; @@ -38,6 +45,10 @@ pub unsafe extern fn kstart() -> ! { static mut __bss_start: u8; /// The ending byte of the _.bss_ (uninitialized data) segment. static mut __bss_end: u8; + /// The thread descriptor. + static mut __tcb: ThreadControlBlock; + /// The end of the kernel + static mut __end: u8; } // Zero BSS, this initializes statics that are set to 0 @@ -50,12 +61,12 @@ pub unsafe extern fn kstart() -> ! { memset(start_ptr, 0, size); } - debug_assert_eq!(BSS_TEST_ZERO, 0); - debug_assert_eq!(BSS_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); + assert_eq!(BSS_TEST_ZERO, 0); + assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); } // Initialize memory management - memory::init(0, &__bss_end as *const u8 as usize); + memory::init(0, &__end as *const u8 as usize); // TODO: allocate a stack let stack_start = 0x00080000; @@ -65,11 +76,21 @@ pub unsafe extern fn kstart() -> ! { let mut active_table = paging::init(stack_start, stack_end); // Set up GDT - gdt::init(); + gdt::init(__tcb.offset); // Set up IDT idt::init(); + // Test tdata and tbss + { + assert_eq!(TBSS_TEST_ZERO, 0); + TBSS_TEST_ZERO += 1; + assert_eq!(TBSS_TEST_ZERO, 1); + assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); + TDATA_TEST_NONZERO -= 1; + assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFE); + } + // Reset AP variables AP_COUNT.store(0, Ordering::SeqCst); BSP_READY.store(false, Ordering::SeqCst); @@ -114,15 +135,28 @@ pub unsafe extern fn kstart() -> ! { /// Entry to rust for an AP pub unsafe extern fn kstart_ap(stack_start: usize, stack_end: usize) -> ! { { + assert_eq!(BSS_TEST_ZERO, 0); + assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); + // Initialize paging let mut active_table = paging::init(stack_start, stack_end); // Set up GDT for AP - gdt::init(); + gdt::init_ap(); // Set up IDT for AP idt::init(); + // Test tdata and tbss + { + assert_eq!(TBSS_TEST_ZERO, 0); + TBSS_TEST_ZERO += 1; + assert_eq!(TBSS_TEST_ZERO, 1); + assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); + TDATA_TEST_NONZERO -= 1; + assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFE); + } + // Map heap { let heap_start_page = Page::containing_address(VirtualAddress::new(HEAP_START)); diff --git a/arch/x86_64/src/tcb.rs b/arch/x86_64/src/tcb.rs new file mode 100644 index 0000000..b4c425e --- /dev/null +++ b/arch/x86_64/src/tcb.rs @@ -0,0 +1,5 @@ +/// Thread control block +#[repr(C)] +pub struct ThreadControlBlock { + pub offset: usize +} diff --git a/bootloader/x86/kernel.ld b/bootloader/x86/kernel.ld index 8a428d2..3f524a8 100644 --- a/bootloader/x86/kernel.ld +++ b/bootloader/x86/kernel.ld @@ -28,26 +28,42 @@ SECTIONS { *(.data*) . = ALIGN(4096); __data_end = .; + } + + .tdata : AT(ADDR(.tdata) - KERNEL_OFFSET) { __tdata_start = .; *(.tdata*) - . = ALIGN(4096); + . = ALIGN(4096); __tdata_end = .; } + .tbss : AT(ADDR(.tbss) - KERNEL_OFFSET) { + __tbss_start = .; + *(.tbss*) + . = ALIGN(4096); + __tbss_end = .; + } + + .tcb : AT(ADDR(.tcb) - KERNEL_OFFSET) { + __tcb = .; + . += 8; + . = ALIGN(4096); + } + .bss : AT(ADDR(.bss) - KERNEL_OFFSET) { __bss_start = .; *(.bss*) . = ALIGN(4096); __bss_end = .; - __tbss_start = .; - *(.tbss*) - . = ALIGN(4096); - __tbss_end = .; } + __end = .; + /DISCARD/ : { *(.comment*) + *(.debug*) *(.eh_frame*) + *(.gcc_except_table*) *(.note*) *(.rel.eh_frame*) } diff --git a/x86_64-unknown-none.json b/x86_64-unknown-none.json index b21b29d..4eb3012 100644 --- a/x86_64-unknown-none.json +++ b/x86_64-unknown-none.json @@ -21,5 +21,5 @@ "no-compiler-rt": true, "no-default-libraries": true, "position-independent-executables": false, - "has-elf-tls": false + "has-elf-tls": true }