Skip to content

Commit

Permalink
Kernel starts on a valid KernelStack
Browse files Browse the repository at this point in the history
It is now the bootstrap's job to allocate a KernelStack for the kernel to start on.
This simplifies a lot the kernel's startup, and safety checks,
as it expects to *always* be running on a KernelStack.
  • Loading branch information
Orycterope committed Sep 29, 2018
1 parent 2ddf096 commit 1870168
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 108 deletions.
41 changes: 17 additions & 24 deletions bootstrap/src/bootstrap_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,31 @@
//!
//! A bootstrap stack is structured as follow :
//!
//! No Page Guard
//! j--------------------j < 0xaaaaffff
//! j--------------------j < 0xaaaa0000 = BootstrapStack.stack_address
//! | |
//! | |
//! | STACK |
//! | PAGE GUARD |
//! | |
//! | |
//! j--------------------j
//! | |
//! | ||| |
//! | VVV |
//! | |
//! | AAA |
//! | ||| |
//! | |
//! j--------------------j
//! | |
//! | STACK |
//! | |
//! | PAGE_GUARD |
//! | |
//! | |
//! j--------------------j < 0xaaaa0000
//! | j----------------j |
//! | | poison value | |
//! j-j----------------j-j < 0xaaaaffff
//! No Page Guard
//!
//! Since the stack is several pages long, we must ensure the stack respects some alignment
//! in order to be able to find its bottom from any page.
//!
//! Must be consistent with KernelStack, as kernel considers it's already running on a KernelStack.

use ::core::mem::size_of;
use paging::*;
Expand Down Expand Up @@ -75,20 +77,11 @@ impl BootstrapStack {
*saved_ebp = 0x00000000;
}

/// Switch to this bootstrap stack.
/// The function passed as parameter will be called with the new stack, and should never return
pub unsafe fn switch_to(self, f: fn() -> !) -> ! {
let new_ebp_esp: usize = self.stack_address.addr() + STACK_SIZE_WITH_GUARD * PAGE_SIZE
- Self::STACK_POISON_SIZE;
asm!("
mov ebp, $0
mov esp, $0
jmp $1"
:
: "r"(new_ebp_esp), "r"(f)
: "memory"
: "intel", "volatile");

unreachable!();
/// Get the address of the beginning of usable stack.
/// Used for initializing $esp and $ebp of a newborn process
/// Points to the last poison pointer, for saved $ebp
pub fn get_stack_start(&self) -> usize {
self.stack_address.addr() + STACK_SIZE_WITH_GUARD * PAGE_SIZE
- Self::STACK_POISON_SIZE
}
}
54 changes: 21 additions & 33 deletions bootstrap/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
//! What the bootstrap stage does is :
//! 1. create a set of pages
//! 2. identity map bootstrap sections
//! 2. enable paging
//! 3. load kernel at the end of address space
//! 3. enable paging
//! 4. load kernel at the end of address space
//! 5. construct a map of kernel sections that will be passed to kernel
//! 4. jump to kernel
//! 6. create a kernel stack
//! 7. jump to kernel
//!
//! ## Logging
//!
Expand Down Expand Up @@ -140,41 +141,28 @@ pub extern "C" fn do_bootstrap(multiboot_info_addr: usize) -> ! {
.expect("Cannot allocate bootstrap stack");
writeln!(Serial, "= Created kernel stack");

// Start using this stack
MULTIBOOT_INFO_PAGE.call_once(|| multiboot_info_page);
KERNEL_ENTRY_POINT.call_once(|| VirtualAddress(kernel_entry_point));
unsafe { new_stack.switch_to(do_bootstrap_continue_stack) }
unreachable!()
}

/// We're about to switch to a new stack, but we want to keep references to some value,
/// so we make them global ...
static MULTIBOOT_INFO_PAGE: Once<VirtualAddress> = Once::new();
static KERNEL_ENTRY_POINT: Once<VirtualAddress> = Once::new();

/// When we switch to a new stack during init, we can't return now that the stack is empty
/// so we need to call some function that will proceed with the end of the init procedure
/// This is some function
pub fn do_bootstrap_continue_stack() -> ! {
writeln!(Serial, "= Switched to new kernel stack");

let multiboot_info = MULTIBOOT_INFO_PAGE.try()
.expect("Multiboot info page was not propagated").addr();
let kernel_entry_point = KERNEL_ENTRY_POINT.try()
.expect("Kernel entry point was not propagated").addr();
let new_ebp_esp = new_stack.get_stack_start();

writeln!(Serial, "= Jumping to kernel");

unsafe {
asm!("mov ebx, $0
jmp $1"
: // no output
: "r"(multiboot_info), "r"(kernel_entry_point)
: "ebx", "memory"
: "intel"
);
asm!("
// save multiboot info pointer
mov ebx, $0
// switch to the new stack
mov ebp, $1
mov esp, $1
// jump to the kernel
jmp $2"
:
: "r"(multiboot_info_page), "r"(new_ebp_esp), "r"(kernel_entry_point)
: "memory"
: "intel", "volatile");
}

unsafe { ::core::intrinsics::unreachable() }
unreachable!()
}

#[cfg(target_os = "none")]
Expand Down
65 changes: 28 additions & 37 deletions kernel/src/i386/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
//!
//! A kernel stack is structured as follow :
//!
//! No Page Guard
//! j--------------------j < 0xaaaa0000 = KernelStack.stack_address
//! | |
//! | |
Expand All @@ -22,6 +21,7 @@
//! | j----------------j |
//! | | poison value | |
//! j-j----------------j-j < 0xaaaaffff
//! No Page Guard
//!
//! Since the stack is several pages long, we must ensure the stack respects some alignment
//! in order to be able to find its bottom from any page.
Expand Down Expand Up @@ -74,11 +74,11 @@ impl KernelStack {

/// Retrieves the current stack from $esp
///
/// # Safety
/// Should be used only to retrieve the KernelStack that was given to us by the bootstrap.
///
/// We must be using a KernelStack ! Not any random stack
/// # Safety
///
/// Also unsafe because it creates duplicates of the stack structure,
/// Unsafe because it creates duplicates of the stack structure,
/// whose only owner should be the ProcessStruct it belongs to.
/// This enables having several mut references pointing to the same underlying memory.
/// Caller has to make sure no references to the stack exists when calling this function.
Expand Down Expand Up @@ -111,49 +111,40 @@ impl KernelStack {
- Self::STACK_POISON_SIZE
}

/// Switch to this kernel stack.
/// The function passed as parameter will be called with the new stack, and should never return
pub unsafe fn switch_to(self, f: fn() -> !) -> ! {
let new_ebp_esp = self.get_stack_start();
asm!("
mov ebp, $0
mov esp, $0
jmp $1"
:
: "r"(new_ebp_esp), "r"(f)
: "memory"
: "intel", "volatile");

unreachable!();
}

/// Dumps the stack on all the Loggers, displaying it in a frame-by-frame format
///
/// # Safety
///
/// We must be using a kernel stack ! Not any random stack
pub unsafe fn dump_current_stack() {
pub fn dump_current_stack() {
let mut ebp;
let mut esp;
let mut eip;
asm!(" mov $0, ebp
mov $1, esp
// eip can only be read through the stack after a call instruction
call read_eip
read_eip:
pop $2"
unsafe {
asm!("
mov $0, ebp
mov $1, esp
// eip can only be read through the stack after a call instruction
call read_eip
read_eip:
pop $2"
: "=r"(ebp), "=r"(esp), "=r"(eip) ::: "volatile", "intel" );
let stack = Self::get_current_stack();
}

let stack_bottom = (stack.stack_address.addr() + PAGE_SIZE) as *const u8;
let stack_slice = ::core::slice::from_raw_parts(stack_bottom,
STACK_SIZE * PAGE_SIZE);
let stack_bottom = (Self::get_current_stack_bottom() + PAGE_SIZE) as *const u8;
let stack_slice = unsafe { ::core::slice::from_raw_parts(stack_bottom,
STACK_SIZE * PAGE_SIZE) };

dump_stack(stack_slice, stack_bottom as usize, esp, ebp, eip);
}
}

// TODO destroy the stack ?
impl Drop for KernelStack {
/// We deallocate the stack when it is dropped
fn drop(&mut self) {
debug!("Dropping KernelStack {:?}", self);
let mut tables = ACTIVE_PAGE_TABLES.lock();
for i in 0..STACK_SIZE_WITH_GUARD {
tables.unmap(self.stack_address + i * PAGE_SIZE);
}
}
}

/* ********************************************************************************************** */
Expand Down
14 changes: 0 additions & 14 deletions kernel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,20 +269,6 @@ pub extern "C" fn common_start(multiboot_info_addr: usize) -> ! {

log_impl::init();

let new_stack = stack::KernelStack::allocate_stack()
.expect("Failed to allocate new kernel stack");
unsafe { new_stack.switch_to(common_start_continue_stack) }
unreachable!()
}

/// When we switch to a new valid kernel stack during init, we can't return now that the stack is empty
/// so we need to call some function that will proceed with the end of the init procedure
/// This is some function
#[cfg(target_os = "none")]
#[no_mangle]
pub fn common_start_continue_stack() -> ! {
info!("Switched to new kernel stack");

unsafe { devices::pit::init_channel_0() };
info!("Initialized PIT");

Expand Down

0 comments on commit 1870168

Please sign in to comment.