From fbbfe167648afaf4f41b3dae9fd8442fdd1eea7d Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 16 Sep 2016 10:44:52 -0600 Subject: [PATCH] Organize context module --- kernel/context/context.rs | 87 ++++++++++++++ kernel/context/list.rs | 80 +++++++++++++ kernel/context/mod.rs | 231 +++----------------------------------- kernel/context/switch.rs | 65 +++++++++++ 4 files changed, 246 insertions(+), 217 deletions(-) create mode 100644 kernel/context/context.rs create mode 100644 kernel/context/list.rs create mode 100644 kernel/context/switch.rs diff --git a/kernel/context/context.rs b/kernel/context/context.rs new file mode 100644 index 0000000..89f8089 --- /dev/null +++ b/kernel/context/context.rs @@ -0,0 +1,87 @@ +use alloc::boxed::Box; +use collections::Vec; + +use arch; +use super::file::File; +use super::memory::Memory; + +/// A context, which identifies either a process or a thread +#[derive(Debug)] +pub struct Context { + /// The ID of this context + pub id: usize, + //TODO: Status enum + /// Running or not + pub running: bool, + /// Blocked or not + pub blocked: bool, + /// Exited or not` + pub exited: bool, + /// The architecture specific context + pub arch: arch::context::Context, + /// Kernel stack + pub kstack: Option>, + /// Executable image + pub image: Vec, + /// User heap + pub heap: Option, + /// User stack + pub stack: Option, + /// The open files in the scheme + pub files: Vec> +} + +impl Context { + /// Create a new context + pub fn new(id: usize) -> Context { + Context { + id: id, + running: false, + blocked: true, + exited: false, + arch: arch::context::Context::new(), + kstack: None, + image: Vec::new(), + heap: None, + stack: None, + files: Vec::new() + } + } + + /// Add a file to the lowest available slot. + /// Return the file descriptor number or None if no slot was found + pub fn add_file(&mut self, file: File) -> Option { + for (i, mut file_option) in self.files.iter_mut().enumerate() { + if file_option.is_none() { + *file_option = Some(file); + return Some(i); + } + } + let len = self.files.len(); + if len < super::CONTEXT_MAX_FILES { + self.files.push(Some(file)); + Some(len) + } else { + None + } + } + + /// Get a file + pub fn get_file(&self, i: usize) -> Option { + if i < self.files.len() { + self.files[i] + } else { + None + } + } + + /// Remove a file + // TODO: adjust files vector to smaller size if possible + pub fn remove_file(&mut self, i: usize) -> Option { + if i < self.files.len() { + self.files[i].take() + } else { + None + } + } +} diff --git a/kernel/context/list.rs b/kernel/context/list.rs new file mode 100644 index 0000000..b9cb972 --- /dev/null +++ b/kernel/context/list.rs @@ -0,0 +1,80 @@ +use alloc::boxed::Box; +use collections::BTreeMap; +use core::mem; +use core::sync::atomic::Ordering; +use spin::RwLock; + +use arch; +use syscall::{Result, Error}; +use super::context::Context; + +/// Context list type +pub struct ContextList { + map: BTreeMap>, + next_id: usize +} + +impl ContextList { + /// Create a new context list. + pub fn new() -> Self { + ContextList { + map: BTreeMap::new(), + next_id: 1 + } + } + + /// Get the nth context. + pub fn get(&self, id: usize) -> Option<&RwLock> { + self.map.get(&id) + } + + /// Get the current context. + pub fn current(&self) -> Option<&RwLock> { + self.map.get(&super::CONTEXT_ID.load(Ordering::SeqCst)) + } + + pub fn iter(&self) -> ::collections::btree_map::Iter> { + self.map.iter() + } + + /// Create a new context. + pub fn new_context(&mut self) -> Result<&RwLock> { + if self.next_id >= super::CONTEXT_MAX_CONTEXTS { + self.next_id = 1; + } + + while self.map.contains_key(&self.next_id) { + self.next_id += 1; + } + + if self.next_id >= super::CONTEXT_MAX_CONTEXTS { + return Err(Error::TryAgain); + } + + let id = self.next_id; + self.next_id += 1; + + assert!(self.map.insert(id, RwLock::new(Context::new(id))).is_none()); + + Ok(self.map.get(&id).expect("Failed to insert new context. ID is out of bounds.")) + } + + /// Spawn a context from a function. + pub fn spawn(&mut self, func: extern fn()) -> Result<&RwLock> { + let context_lock = self.new_context()?; + { + let mut context = context_lock.write(); + let mut stack = Box::new([0; 65536]); + let offset = stack.len() - mem::size_of::(); + unsafe { + let offset = stack.len() - mem::size_of::(); + let func_ptr = stack.as_mut_ptr().offset(offset as isize); + *(func_ptr as *mut usize) = func as usize; + } + context.arch.set_page_table(unsafe { arch::paging::ActivePageTable::new().address() }); + context.arch.set_stack(stack.as_ptr() as usize + offset); + context.kstack = Some(stack); + } + Ok(context_lock) + } +} diff --git a/kernel/context/mod.rs b/kernel/context/mod.rs index e243123..d25ea81 100644 --- a/kernel/context/mod.rs +++ b/kernel/context/mod.rs @@ -1,19 +1,25 @@ //! Context management -use alloc::boxed::Box; -use collections::{BTreeMap, Vec}; -use core::mem; use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use arch; -use arch::context::Context as ArchContext; -use syscall::{Error, Result}; +pub use self::context::Context; +pub use self::list::ContextList; +pub use self::switch::switch; -/// File operations +/// Context struct +mod context; + +/// Context list +mod list; + +/// Context switch function +mod switch; + +/// File struct - defines a scheme and a file number pub mod file; -/// Memory operations +/// Memory struct - contains a set of pages for a context pub mod memory; /// Limit on number of contexts @@ -22,73 +28,6 @@ pub const CONTEXT_MAX_CONTEXTS: usize = 65536; /// Maximum context files pub const CONTEXT_MAX_FILES: usize = 65536; -/// Context list type -pub struct ContextList { - map: BTreeMap>, - next_id: usize -} - -impl ContextList { - /// Create a new context list. - pub fn new() -> Self { - ContextList { - map: BTreeMap::new(), - next_id: 1 - } - } - - /// Get the nth context. - pub fn get(&self, id: usize) -> Option<&RwLock> { - self.map.get(&id) - } - - /// Get the current context. - pub fn current(&self) -> Option<&RwLock> { - self.map.get(&CONTEXT_ID.load(Ordering::SeqCst)) - } - - /// Create a new context. - pub fn new_context(&mut self) -> Result<&RwLock> { - if self.next_id >= CONTEXT_MAX_CONTEXTS { - self.next_id = 1; - } - - while self.map.contains_key(&self.next_id) { - self.next_id += 1; - } - - if self.next_id >= CONTEXT_MAX_CONTEXTS { - return Err(Error::TryAgain); - } - - let id = self.next_id; - self.next_id += 1; - - assert!(self.map.insert(id, RwLock::new(Context::new(id))).is_none()); - - Ok(self.map.get(&id).expect("Failed to insert new context. ID is out of bounds.")) - } - - /// Spawn a context from a function. - pub fn spawn(&mut self, func: extern fn()) -> Result<&RwLock> { - let context_lock = self.new_context()?; - { - let mut context = context_lock.write(); - let mut stack = Box::new([0; 65536]); - let offset = stack.len() - mem::size_of::(); - unsafe { - let offset = stack.len() - mem::size_of::(); - let func_ptr = stack.as_mut_ptr().offset(offset as isize); - *(func_ptr as *mut usize) = func as usize; - } - context.arch.set_page_table(unsafe { arch::paging::ActivePageTable::new().address() }); - context.arch.set_stack(stack.as_ptr() as usize + offset); - context.kstack = Some(stack); - } - Ok(context_lock) - } -} - /// Contexts list static CONTEXTS: Once> = Once::new(); @@ -118,145 +57,3 @@ pub fn contexts() -> RwLockReadGuard<'static, ContextList> { pub fn contexts_mut() -> RwLockWriteGuard<'static, ContextList> { CONTEXTS.call_once(init_contexts).write() } - -/// Switch to the next context -/// -/// # Safety -/// -/// Do not call this while holding locks! -pub unsafe fn switch() { - use core::ops::DerefMut; - - // Set the global lock to avoid the unsafe operations below from causing issues - while arch::context::CONTEXT_SWITCH_LOCK.compare_and_swap(false, true, Ordering::SeqCst) { - arch::interrupt::pause(); - } - - let from_ptr = { - let contexts = contexts(); - let context_lock = contexts.current().expect("context::switch: Not inside of context"); - let mut context = context_lock.write(); - context.deref_mut() as *mut Context - }; - - let mut to_ptr = 0 as *mut Context; - - for (pid, context_lock) in contexts().map.iter() { - if *pid > (*from_ptr).id { - let mut context = context_lock.write(); - if ! context.running && ! context.blocked && ! context.exited { - to_ptr = context.deref_mut() as *mut Context; - break; - } - } - } - - if to_ptr as usize == 0 { - for (pid, context_lock) in contexts().map.iter() { - if *pid < (*from_ptr).id { - let mut context = context_lock.write(); - if ! context.running && ! context.blocked && ! context.exited { - to_ptr = context.deref_mut() as *mut Context; - break; - } - } - } - } - - if to_ptr as usize == 0 { - // TODO: Sleep, wait for interrupt - // Unset global lock if no context found - arch::context::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst); - return; - } - - (&mut *from_ptr).running = false; - (&mut *to_ptr).running = true; - if let Some(ref stack) = (*to_ptr).kstack { - arch::gdt::TSS.rsp[0] = (stack.as_ptr() as usize + stack.len() - 256) as u64; - } - CONTEXT_ID.store((&mut *to_ptr).id, Ordering::SeqCst); - - (&mut *from_ptr).arch.switch_to(&mut (&mut *to_ptr).arch); -} - -/// A context, which identifies either a process or a thread -#[derive(Debug)] -pub struct Context { - /// The ID of this context - pub id: usize, - //TODO: Status enum - /// Running or not - pub running: bool, - /// Blocked or not - pub blocked: bool, - /// Exited or not` - pub exited: bool, - /// The architecture specific context - pub arch: ArchContext, - /// Kernel stack - pub kstack: Option>, - /// Executable image - pub image: Vec, - /// User heap - pub heap: Option, - /// User stack - pub stack: Option, - /// The open files in the scheme - pub files: Vec> -} - -impl Context { - /// Create a new context - pub fn new(id: usize) -> Context { - Context { - id: id, - running: false, - blocked: true, - exited: false, - arch: ArchContext::new(), - kstack: None, - image: Vec::new(), - heap: None, - stack: None, - files: Vec::new() - } - } - - /// Add a file to the lowest available slot. - /// Return the file descriptor number or None if no slot was found - pub fn add_file(&mut self, file: file::File) -> Option { - for (i, mut file_option) in self.files.iter_mut().enumerate() { - if file_option.is_none() { - *file_option = Some(file); - return Some(i); - } - } - let len = self.files.len(); - if len < CONTEXT_MAX_FILES { - self.files.push(Some(file)); - Some(len) - } else { - None - } - } - - /// Get a file - pub fn get_file(&self, i: usize) -> Option { - if i < self.files.len() { - self.files[i] - } else { - None - } - } - - /// Remove a file - // TODO: adjust files vector to smaller size if possible - pub fn remove_file(&mut self, i: usize) -> Option { - if i < self.files.len() { - self.files[i].take() - } else { - None - } - } -} diff --git a/kernel/context/switch.rs b/kernel/context/switch.rs new file mode 100644 index 0000000..db168d0 --- /dev/null +++ b/kernel/context/switch.rs @@ -0,0 +1,65 @@ +use core::sync::atomic::Ordering; + +use arch; +use super::{contexts, Context, CONTEXT_ID}; + +/// Switch to the next context +/// +/// # Safety +/// +/// Do not call this while holding locks! +pub unsafe fn switch() { + use core::ops::DerefMut; + + // Set the global lock to avoid the unsafe operations below from causing issues + while arch::context::CONTEXT_SWITCH_LOCK.compare_and_swap(false, true, Ordering::SeqCst) { + arch::interrupt::pause(); + } + + let from_ptr = { + let contexts = contexts(); + let context_lock = contexts.current().expect("context::switch: Not inside of context"); + let mut context = context_lock.write(); + context.deref_mut() as *mut Context + }; + + let mut to_ptr = 0 as *mut Context; + + for (pid, context_lock) in contexts().iter() { + if *pid > (*from_ptr).id { + let mut context = context_lock.write(); + if ! context.running && ! context.blocked && ! context.exited { + to_ptr = context.deref_mut() as *mut Context; + break; + } + } + } + + if to_ptr as usize == 0 { + for (pid, context_lock) in contexts().iter() { + if *pid < (*from_ptr).id { + let mut context = context_lock.write(); + if ! context.running && ! context.blocked && ! context.exited { + to_ptr = context.deref_mut() as *mut Context; + break; + } + } + } + } + + if to_ptr as usize == 0 { + // TODO: Sleep, wait for interrupt + // Unset global lock if no context found + arch::context::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst); + return; + } + + (&mut *from_ptr).running = false; + (&mut *to_ptr).running = true; + if let Some(ref stack) = (*to_ptr).kstack { + arch::gdt::TSS.rsp[0] = (stack.as_ptr() as usize + stack.len() - 256) as u64; + } + CONTEXT_ID.store((&mut *to_ptr).id, Ordering::SeqCst); + + (&mut *from_ptr).arch.switch_to(&mut (&mut *to_ptr).arch); +}