WIP: Thread control block and TSS in kernel
This commit is contained in:
parent
0501b2580d
commit
8ddd0ad3f0
|
@ -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::<GdtEntry>() - 1) as u16;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
5
arch/x86_64/src/tcb.rs
Normal file
5
arch/x86_64/src/tcb.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// Thread control block
|
||||
#[repr(C)]
|
||||
pub struct ThreadControlBlock {
|
||||
pub offset: usize
|
||||
}
|
|
@ -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*)
|
||||
}
|
||||
|
|
|
@ -21,5 +21,5 @@
|
|||
"no-compiler-rt": true,
|
||||
"no-default-libraries": true,
|
||||
"position-independent-executables": false,
|
||||
"has-elf-tls": false
|
||||
"has-elf-tls": true
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue