WIP: Thread control block and TSS in kernel

This commit is contained in:
Jeremy Soller 2016-08-19 17:38:37 -06:00
parent 0501b2580d
commit 8ddd0ad3f0
8 changed files with 161 additions and 55 deletions

View file

@ -3,16 +3,17 @@
use core::mem; use core::mem;
use x86::dtables::{self, DescriptorTablePointer}; use x86::dtables::{self, DescriptorTablePointer};
use x86::segmentation::{self, SegmentSelector}; use x86::segmentation::{self, SegmentSelector};
use x86::task::{self, TaskStateSegment}; use x86::task::TaskStateSegment;
pub const GDT_NULL: usize = 0; pub const GDT_NULL: usize = 0;
pub const GDT_KERNEL_CODE: usize = 1; pub const GDT_KERNEL_CODE: usize = 1;
pub const GDT_KERNEL_DATA: usize = 2; pub const GDT_KERNEL_DATA: usize = 2;
pub const GDT_USER_CODE: usize = 3; pub const GDT_KERNEL_TLS: usize = 3;
pub const GDT_USER_DATA: usize = 4; pub const GDT_USER_CODE: usize = 4;
pub const GDT_USER_TLS: usize = 5; pub const GDT_USER_DATA: usize = 5;
pub const GDT_TSS: usize = 6; pub const GDT_USER_TLS: usize = 6;
pub const GDT_TSS_HIGH: usize = 7; 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_PRESENT: u8 = 1 << 7;
pub const GDT_A_RING_0: u8 = 0 << 5; pub const GDT_A_RING_0: u8 = 0 << 5;
@ -37,13 +38,15 @@ pub static mut GDTR: DescriptorTablePointer = DescriptorTablePointer {
base: 0 base: 0
}; };
pub static mut GDT: [GdtEntry; 8] = [ pub static mut GDT: [GdtEntry; 9] = [
// Null // Null
GdtEntry::new(0, 0, 0, 0), GdtEntry::new(0, 0, 0, 0),
// Kernel code // 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), 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 // Kernel data
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), 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 // 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), 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 // User data
@ -51,7 +54,7 @@ pub static mut GDT: [GdtEntry; 8] = [
//TODO: User TLS //TODO: User TLS
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE), GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
//TODO: TSS //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 // TSS must be 16 bytes long, twice the normal size
GdtEntry::new(0, 0, 0, 0), GdtEntry::new(0, 0, 0, 0),
]; ];
@ -66,18 +69,25 @@ pub static mut TSS: TaskStateSegment = TaskStateSegment {
iomap_base: 0xFFFF iomap_base: 0xFFFF
}; };
pub unsafe fn init() { pub unsafe fn init(tcb_offset: usize) {
GDTR.limit = (GDT.len() * mem::size_of::<GdtEntry>() - 1) as u16; GDTR.limit = (GDT.len() * mem::size_of::<GdtEntry>() - 1) as u16;
GDTR.base = GDT.as_ptr() as u64; GDTR.base = GDT.as_ptr() as u64;
GDT[GDT_TSS] = GdtEntry::new(&TSS as *const _ as u32, mem::size_of::<TaskStateSegment>() 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::<TaskStateSegment>() as u32);
init_ap();
}
pub unsafe fn init_ap() {
dtables::lgdt(&GDTR); dtables::lgdt(&GDTR);
segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16)); segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16));
segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16)); segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16));
segmentation::load_es(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_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16));
segmentation::load_ss(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 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;
}
} }

View file

@ -44,7 +44,8 @@ macro_rules! interrupt {
} }
// Push scratch registers // Push scratch registers
asm!("push rax asm!("xchg bx, bx
push rax
push rcx push rcx
push rdx push rdx
push rdi push rdi
@ -85,7 +86,8 @@ macro_rules! interrupt_error {
} }
// Push scratch registers // Push scratch registers
asm!("push rax asm!("xchg bx, bx
push rax
push rcx push rcx
push rdx push rdx
push rdi push rdi
@ -148,3 +150,6 @@ pub mod serial;
/// Initialization and start function /// Initialization and start function
pub mod start; pub mod start;
/// Thread control block
pub mod tcb;

View file

@ -33,7 +33,8 @@ impl Mapper {
let mut p1 = p2.next_table_create(page.p2_index()); let mut p1 = p2.next_table_create(page.p2_index());
assert!(p1[page.p1_index()].is_unused(), 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(), p1[page.p1_index()].address().get(), p1[page.p1_index()].flags(),
frame.start_address().get(), flags); frame.start_address().get(), flags);
p1[page.p1_index()].set(frame, flags | entry::PRESENT); p1[page.p1_index()].set(frame, flags | entry::PRESENT);

View file

@ -2,8 +2,10 @@
//! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/modifying-page-tables.html) //! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/modifying-page-tables.html)
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use x86::tlb;
use memory::{allocate_frame, Frame}; use memory::{allocate_frame, Frame};
use tcb::ThreadControlBlock;
use self::entry::{EntryFlags, PRESENT, WRITABLE, NO_EXECUTE}; use self::entry::{EntryFlags, PRESENT, WRITABLE, NO_EXECUTE};
use self::mapper::Mapper; use self::mapper::Mapper;
@ -43,6 +45,8 @@ pub unsafe fn init(stack_start: usize, stack_end: usize) -> ActivePageTable {
static mut __tbss_start: u8; static mut __tbss_start: u8;
/// The ending byte of the thread BSS segment /// The ending byte of the thread BSS segment
static mut __tbss_end: u8; 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. /// The starting byte of the _.bss_ (uninitialized data) segment.
static mut __bss_start: u8; static mut __bss_start: u8;
/// The ending byte of the _.bss_ (uninitialized data) segment. /// The ending byte of the _.bss_ (uninitialized data) segment.
@ -61,11 +65,13 @@ pub unsafe fn init(stack_start: usize, stack_end: usize) -> ActivePageTable {
active_table.with(&mut new_table, &mut temporary_page, |mapper| { active_table.with(&mut new_table, &mut temporary_page, |mapper| {
{ {
let mut remap = |start: usize, end: usize, flags: EntryFlags| { let mut remap = |start: usize, end: usize, flags: EntryFlags| {
if end > start {
let start_frame = Frame::containing_address(PhysicalAddress::new(start)); let start_frame = Frame::containing_address(PhysicalAddress::new(start));
let end_frame = Frame::containing_address(PhysicalAddress::new(end - 1)); let end_frame = Frame::containing_address(PhysicalAddress::new(end - 1));
for frame in Frame::range_inclusive(start_frame, end_frame) { for frame in Frame::range_inclusive(start_frame, end_frame) {
mapper.identity_map(frame, flags); mapper.identity_map(frame, flags);
} }
}
}; };
// Remap stack writable, no execute // Remap stack writable, no execute
@ -90,41 +96,59 @@ pub unsafe fn init(stack_start: usize, stack_end: usize) -> ActivePageTable {
// Map and copy TDATA // Map and copy TDATA
{ {
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); 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 start_page = Page::containing_address(VirtualAddress::new(start));
let end_page = Page::containing_address(VirtualAddress::new(end - 1)); let end_page = Page::containing_address(VirtualAddress::new(end - 1));
for page in Page::range_inclusive(start_page, end_page) { for page in Page::range_inclusive(start_page, end_page) {
// Copy master to temporary page // Copy parent to temporary page
{ {
let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get())); let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get()));
active_table.identity_map(frame, PRESENT | NO_EXECUTE); 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); ::externs::memcpy(temporary_page.start_address().get() as *mut u8, page.start_address().get() as *const u8, 4096);
active_table.unmap(page); active_table.unmap(page);
} }
// Copy temporary page to CPU copy // Copy temporary page to child
{ {
active_table.map(page, PRESENT | NO_EXECUTE | WRITABLE); 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); ::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 // Map and clear TBSS
{ {
let start = & __tbss_start as *const _ as usize; let start = & __tbss_start as *const _ as usize;
let end = & __tbss_end as *const _ as usize; let end = & __tbss_end as *const _ as usize;
if end > start {
let start_page = Page::containing_address(VirtualAddress::new(start)); let start_page = Page::containing_address(VirtualAddress::new(start));
let end_page = Page::containing_address(VirtualAddress::new(end - 1)); let end_page = Page::containing_address(VirtualAddress::new(end - 1));
for page in Page::range_inclusive(start_page, end_page) { for page in Page::range_inclusive(start_page, end_page) {
active_table.map(page, PRESENT | NO_EXECUTE | WRITABLE); active_table.map(page, PRESENT | NO_EXECUTE | WRITABLE);
tlb::flush_all();
::externs::memset(page.start_address().get() as *mut u8, 0, 4096); ::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 active_table
} }

View file

@ -12,11 +12,18 @@ use gdt;
use idt; use idt;
use memory::{self, Frame}; use memory::{self, Frame};
use paging::{self, entry, Page, PhysicalAddress, VirtualAddress}; use paging::{self, entry, Page, PhysicalAddress, VirtualAddress};
use tcb::ThreadControlBlock;
/// Test of zero values in BSS. /// Test of zero values in BSS.
static BSS_TEST_ZERO: usize = 0; static BSS_TEST_ZERO: usize = 0;
/// Test of non-zero values in BSS. /// Test of non-zero values in data.
static BSS_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF; 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 AP_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
static BSP_READY: AtomicBool = ATOMIC_BOOL_INIT; static BSP_READY: AtomicBool = ATOMIC_BOOL_INIT;
@ -38,6 +45,10 @@ pub unsafe extern fn kstart() -> ! {
static mut __bss_start: u8; static mut __bss_start: u8;
/// The ending byte of the _.bss_ (uninitialized data) segment. /// The ending byte of the _.bss_ (uninitialized data) segment.
static mut __bss_end: u8; 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 // Zero BSS, this initializes statics that are set to 0
@ -50,12 +61,12 @@ pub unsafe extern fn kstart() -> ! {
memset(start_ptr, 0, size); memset(start_ptr, 0, size);
} }
debug_assert_eq!(BSS_TEST_ZERO, 0); assert_eq!(BSS_TEST_ZERO, 0);
debug_assert_eq!(BSS_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
} }
// Initialize memory management // 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 // TODO: allocate a stack
let stack_start = 0x00080000; let stack_start = 0x00080000;
@ -65,11 +76,21 @@ pub unsafe extern fn kstart() -> ! {
let mut active_table = paging::init(stack_start, stack_end); let mut active_table = paging::init(stack_start, stack_end);
// Set up GDT // Set up GDT
gdt::init(); gdt::init(__tcb.offset);
// Set up IDT // Set up IDT
idt::init(); 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 // Reset AP variables
AP_COUNT.store(0, Ordering::SeqCst); AP_COUNT.store(0, Ordering::SeqCst);
BSP_READY.store(false, Ordering::SeqCst); BSP_READY.store(false, Ordering::SeqCst);
@ -114,15 +135,28 @@ pub unsafe extern fn kstart() -> ! {
/// Entry to rust for an AP /// Entry to rust for an AP
pub unsafe extern fn kstart_ap(stack_start: usize, stack_end: usize) -> ! { 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 // Initialize paging
let mut active_table = paging::init(stack_start, stack_end); let mut active_table = paging::init(stack_start, stack_end);
// Set up GDT for AP // Set up GDT for AP
gdt::init(); gdt::init_ap();
// Set up IDT for AP // Set up IDT for AP
idt::init(); 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 // Map heap
{ {
let heap_start_page = Page::containing_address(VirtualAddress::new(HEAP_START)); let heap_start_page = Page::containing_address(VirtualAddress::new(HEAP_START));

5
arch/x86_64/src/tcb.rs Normal file
View file

@ -0,0 +1,5 @@
/// Thread control block
#[repr(C)]
pub struct ThreadControlBlock {
pub offset: usize
}

View file

@ -28,26 +28,42 @@ SECTIONS {
*(.data*) *(.data*)
. = ALIGN(4096); . = ALIGN(4096);
__data_end = .; __data_end = .;
}
.tdata : AT(ADDR(.tdata) - KERNEL_OFFSET) {
__tdata_start = .; __tdata_start = .;
*(.tdata*) *(.tdata*)
. = ALIGN(4096); . = ALIGN(4096);
__tdata_end = .; __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 : AT(ADDR(.bss) - KERNEL_OFFSET) {
__bss_start = .; __bss_start = .;
*(.bss*) *(.bss*)
. = ALIGN(4096); . = ALIGN(4096);
__bss_end = .; __bss_end = .;
__tbss_start = .;
*(.tbss*)
. = ALIGN(4096);
__tbss_end = .;
} }
__end = .;
/DISCARD/ : { /DISCARD/ : {
*(.comment*) *(.comment*)
*(.debug*)
*(.eh_frame*) *(.eh_frame*)
*(.gcc_except_table*)
*(.note*) *(.note*)
*(.rel.eh_frame*) *(.rel.eh_frame*)
} }

View file

@ -21,5 +21,5 @@
"no-compiler-rt": true, "no-compiler-rt": true,
"no-default-libraries": true, "no-default-libraries": true,
"position-independent-executables": false, "position-independent-executables": false,
"has-elf-tls": false "has-elf-tls": true
} }