Cleanup Redox repo, update Rust, remove old target
This commit is contained in:
parent
98c76d36fd
commit
7cd2eff74c
97 changed files with 24 additions and 79 deletions
108
kernel/src/common/int_like.rs
Normal file
108
kernel/src/common/int_like.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
//! Helpers used to define types that are backed by integers (typically `usize`),
|
||||
//! without compromising safety.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! /// Define an opaque type `Pid` backed by a `usize`.
|
||||
//! int_like!(Pid, usize);
|
||||
//!
|
||||
//! const ZERO: Pid = Pid::from(0);
|
||||
//! ```
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! /// Define opaque types `Pid` and `AtomicPid`, backed respectively by a `usize`
|
||||
//! /// and a `AtomicUsize`.
|
||||
//!
|
||||
//! int_like!(Pid, AtomicPid, usize, AtomicUsize);
|
||||
//!
|
||||
//! const ZERO: Pid = Pid::from(0);
|
||||
//! let ATOMIC_PID: AtomicPid = AtomicPid::default();
|
||||
//! ```
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! int_like {
|
||||
($new_type_name:ident, $backing_type: ident) => {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
pub struct $new_type_name($backing_type);
|
||||
|
||||
impl $new_type_name {
|
||||
pub const fn into(self) -> $backing_type {
|
||||
self.0
|
||||
}
|
||||
pub const fn from(x: $backing_type) -> Self {
|
||||
$new_type_name(x)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($new_type_name:ident, $new_atomic_type_name: ident, $backing_type:ident, $backing_atomic_type:ident) => {
|
||||
int_like!($new_type_name, $backing_type);
|
||||
|
||||
/// A mutable holder for T that can safely be shared among threads.
|
||||
/// Runtime equivalent to using `AtomicUsize`, just type-safer.
|
||||
pub struct $new_atomic_type_name {
|
||||
container: $backing_atomic_type,
|
||||
}
|
||||
|
||||
impl $new_atomic_type_name {
|
||||
pub const fn new(x: $new_type_name) -> Self {
|
||||
$new_atomic_type_name {
|
||||
container: $backing_atomic_type::new(x.into())
|
||||
}
|
||||
}
|
||||
pub const fn default() -> Self {
|
||||
Self::new($new_type_name::from(0))
|
||||
}
|
||||
pub fn load(&self, order: ::core::sync::atomic::Ordering) -> $new_type_name {
|
||||
$new_type_name::from(self.container.load(order))
|
||||
}
|
||||
pub fn store(&self, val: $new_type_name, order: ::core::sync::atomic::Ordering) {
|
||||
self.container.store(val.into(), order)
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn swap(&self, val: $new_type_name, order: ::core::sync::atomic::Ordering) -> $new_type_name {
|
||||
$new_type_name::from(self.container.swap(val.into(), order))
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn compare_and_swap(&self, current: $new_type_name, new: $new_type_name, order: ::core::sync::atomic::Ordering) -> $new_type_name {
|
||||
$new_type_name::from(self.container.compare_and_swap(current.into(), new.into(), order))
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn compare_exchange(&self, current: $new_type_name, new: $new_type_name, success: ::core::sync::atomic::Ordering, failure: ::core::sync::atomic::Ordering) -> ::core::result::Result<$new_type_name, $new_type_name> {
|
||||
match self.container.compare_exchange(current.into(), new.into(), success, failure) {
|
||||
Ok(result) => Ok($new_type_name::from(result)),
|
||||
Err(result) => Err($new_type_name::from(result))
|
||||
}
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn compare_exchange_weak(&self, current: $new_type_name, new: $new_type_name, success: ::core::sync::atomic::Ordering, failure: ::core::sync::atomic::Ordering) -> ::core::result::Result<$new_type_name, $new_type_name> {
|
||||
match self.container.compare_exchange_weak(current.into(), new.into(), success, failure) {
|
||||
Ok(result) => Ok($new_type_name::from(result)),
|
||||
Err(result) => Err($new_type_name::from(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test() {
|
||||
use core::mem::size_of;
|
||||
use ::core::sync::atomic::AtomicUsize;
|
||||
|
||||
// Generate type `usize_like`.
|
||||
int_like!(UsizeLike, usize);
|
||||
const ZERO: UsizeLike = UsizeLike::from(0);
|
||||
assert_eq!(size_of::<UsizeLike>(), size_of::<usize>());
|
||||
|
||||
|
||||
// Generate types `usize_like` and `AtomicUsize`.
|
||||
int_like!(UsizeLike2, AtomicUsizeLike, usize, AtomicUsize);
|
||||
assert_eq!(size_of::<UsizeLike2>(), size_of::<usize>());
|
||||
assert_eq!(size_of::<AtomicUsizeLike>(), size_of::<AtomicUsize>());
|
||||
}
|
||||
|
||||
|
2
kernel/src/common/mod.rs
Normal file
2
kernel/src/common/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
#[macro_use]
|
||||
pub mod int_like;
|
252
kernel/src/context/context.rs
Normal file
252
kernel/src/context/context.rs
Normal file
|
@ -0,0 +1,252 @@
|
|||
use alloc::arc::Arc;
|
||||
use alloc::boxed::Box;
|
||||
use collections::{BTreeMap, Vec, VecDeque};
|
||||
use spin::Mutex;
|
||||
|
||||
use arch;
|
||||
use context::file::File;
|
||||
use context::memory::{Grant, Memory, SharedMemory, Tls};
|
||||
use scheme::{SchemeNamespace, FileHandle};
|
||||
use syscall::data::Event;
|
||||
use sync::{WaitMap, WaitQueue};
|
||||
|
||||
/// Unique identifier for a context (i.e. `pid`).
|
||||
use ::core::sync::atomic::AtomicUsize;
|
||||
int_like!(ContextId, AtomicContextId, usize, AtomicUsize);
|
||||
|
||||
/// The status of a context - used for scheduling
|
||||
/// See syscall::process::waitpid and the sync module for examples of usage
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Status {
|
||||
Runnable,
|
||||
Blocked,
|
||||
Exited(usize)
|
||||
}
|
||||
|
||||
/// A context, which identifies either a process or a thread
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
/// The ID of this context
|
||||
pub id: ContextId,
|
||||
/// The ID of the parent context
|
||||
pub ppid: ContextId,
|
||||
/// The real user id
|
||||
pub ruid: u32,
|
||||
/// The real group id
|
||||
pub rgid: u32,
|
||||
/// The real namespace id
|
||||
pub rns: SchemeNamespace,
|
||||
/// The effective user id
|
||||
pub euid: u32,
|
||||
/// The effective group id
|
||||
pub egid: u32,
|
||||
/// The effective namespace id
|
||||
pub ens: SchemeNamespace,
|
||||
/// Status of context
|
||||
pub status: Status,
|
||||
/// Context running or not
|
||||
pub running: bool,
|
||||
/// CPU ID, if locked
|
||||
pub cpu_id: Option<usize>,
|
||||
/// Context is halting parent
|
||||
pub vfork: bool,
|
||||
/// Context is being waited on
|
||||
pub waitpid: Arc<WaitMap<ContextId, usize>>,
|
||||
/// Context should handle pending signals
|
||||
pub pending: VecDeque<u8>,
|
||||
/// Context should wake up at specified time
|
||||
pub wake: Option<(u64, u64)>,
|
||||
/// The architecture specific context
|
||||
pub arch: arch::context::Context,
|
||||
/// Kernel FX - used to store SIMD and FPU registers on context switch
|
||||
pub kfx: Option<Box<[u8]>>,
|
||||
/// Kernel stack
|
||||
pub kstack: Option<Box<[u8]>>,
|
||||
/// Executable image
|
||||
pub image: Vec<SharedMemory>,
|
||||
/// User heap
|
||||
pub heap: Option<SharedMemory>,
|
||||
/// User stack
|
||||
pub stack: Option<Memory>,
|
||||
/// User Thread local storage
|
||||
pub tls: Option<Tls>,
|
||||
/// User grants
|
||||
pub grants: Arc<Mutex<Vec<Grant>>>,
|
||||
/// The name of the context
|
||||
pub name: Arc<Mutex<Vec<u8>>>,
|
||||
/// The current working directory
|
||||
pub cwd: Arc<Mutex<Vec<u8>>>,
|
||||
/// Kernel events
|
||||
pub events: Arc<WaitQueue<Event>>,
|
||||
/// The process environment
|
||||
pub env: Arc<Mutex<BTreeMap<Box<[u8]>, Arc<Mutex<Vec<u8>>>>>>,
|
||||
/// The open files in the scheme
|
||||
pub files: Arc<Mutex<Vec<Option<File>>>>
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(id: ContextId) -> Context {
|
||||
Context {
|
||||
id: id,
|
||||
ppid: ContextId::from(0),
|
||||
ruid: 0,
|
||||
rgid: 0,
|
||||
rns: SchemeNamespace::from(0),
|
||||
euid: 0,
|
||||
egid: 0,
|
||||
ens: SchemeNamespace::from(0),
|
||||
status: Status::Blocked,
|
||||
running: false,
|
||||
cpu_id: None,
|
||||
vfork: false,
|
||||
waitpid: Arc::new(WaitMap::new()),
|
||||
pending: VecDeque::new(),
|
||||
wake: None,
|
||||
arch: arch::context::Context::new(),
|
||||
kfx: None,
|
||||
kstack: None,
|
||||
image: Vec::new(),
|
||||
heap: None,
|
||||
stack: None,
|
||||
tls: None,
|
||||
grants: Arc::new(Mutex::new(Vec::new())),
|
||||
name: Arc::new(Mutex::new(Vec::new())),
|
||||
cwd: Arc::new(Mutex::new(Vec::new())),
|
||||
events: Arc::new(WaitQueue::new()),
|
||||
env: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
files: Arc::new(Mutex::new(Vec::new()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a relative path absolute
|
||||
/// Given a cwd of "scheme:/path"
|
||||
/// This function will turn "foo" into "scheme:/path/foo"
|
||||
/// "/foo" will turn into "scheme:/foo"
|
||||
/// "bar:/foo" will be used directly, as it is already absolute
|
||||
pub fn canonicalize(&self, path: &[u8]) -> Vec<u8> {
|
||||
if path.iter().position(|&b| b == b':').is_none() {
|
||||
let cwd = self.cwd.lock();
|
||||
if path == b"." {
|
||||
cwd.clone()
|
||||
} else if path == b".." {
|
||||
cwd[..cwd[..cwd.len() - 1]
|
||||
.iter().rposition(|&b| b == b'/' || b == b':')
|
||||
.map_or(cwd.len(), |i| i + 1)]
|
||||
.to_vec()
|
||||
} else if path.starts_with(b"./") {
|
||||
let mut canon = cwd.clone();
|
||||
if ! canon.ends_with(b"/") {
|
||||
canon.push(b'/');
|
||||
}
|
||||
canon.extend_from_slice(&path[2..]);
|
||||
canon
|
||||
} else if path.starts_with(b"../") {
|
||||
let mut canon = cwd[..cwd[..cwd.len() - 1]
|
||||
.iter().rposition(|&b| b == b'/' || b == b':')
|
||||
.map_or(cwd.len(), |i| i + 1)]
|
||||
.to_vec();
|
||||
canon.extend_from_slice(&path[3..]);
|
||||
canon
|
||||
} else if path.starts_with(b"/") {
|
||||
let mut canon = cwd[..cwd.iter().position(|&b| b == b':').map_or(1, |i| i + 1)].to_vec();
|
||||
canon.extend_from_slice(&path);
|
||||
canon
|
||||
} else {
|
||||
let mut canon = cwd.clone();
|
||||
if ! canon.ends_with(b"/") {
|
||||
canon.push(b'/');
|
||||
}
|
||||
canon.extend_from_slice(&path);
|
||||
canon
|
||||
}
|
||||
} else {
|
||||
path.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// Block the context, and return true if it was runnable before being blocked
|
||||
pub fn block(&mut self) -> bool {
|
||||
if self.status == Status::Runnable {
|
||||
self.status = Status::Blocked;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Unblock context, and return true if it was blocked before being marked runnable
|
||||
pub fn unblock(&mut self) -> bool {
|
||||
if self.status == Status::Blocked {
|
||||
self.status = Status::Runnable;
|
||||
if let Some(cpu_id) = self.cpu_id {
|
||||
if cpu_id != ::cpu_id() {
|
||||
// Send IPI if not on current CPU
|
||||
// TODO: Make this more architecture independent
|
||||
unsafe { arch::device::local_apic::LOCAL_APIC.ipi(cpu_id) };
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a file to the lowest available slot.
|
||||
/// Return the file descriptor number or None if no slot was found
|
||||
pub fn add_file(&self, file: File) -> Option<FileHandle> {
|
||||
let mut files = self.files.lock();
|
||||
for (i, mut file_option) in files.iter_mut().enumerate() {
|
||||
if file_option.is_none() {
|
||||
*file_option = Some(file);
|
||||
return Some(FileHandle::from(i));
|
||||
}
|
||||
}
|
||||
let len = files.len();
|
||||
if len < super::CONTEXT_MAX_FILES {
|
||||
files.push(Some(file));
|
||||
Some(FileHandle::from(len))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a file
|
||||
pub fn get_file(&self, i: FileHandle) -> Option<File> {
|
||||
let files = self.files.lock();
|
||||
if i.into() < files.len() {
|
||||
files[i.into()]
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a file with a specific handle number. This is used by dup2
|
||||
/// Return the file descriptor number or None if the slot was not empty, or i was invalid
|
||||
pub fn insert_file(&self, i: FileHandle, file: File) -> Option<FileHandle> {
|
||||
let mut files = self.files.lock();
|
||||
if i.into() < super::CONTEXT_MAX_FILES {
|
||||
while i.into() >= files.len() {
|
||||
files.push(None);
|
||||
}
|
||||
if files[i.into()].is_none() {
|
||||
files[i.into()] = Some(file);
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a file
|
||||
// TODO: adjust files vector to smaller size if possible
|
||||
pub fn remove_file(&self, i: FileHandle) -> Option<File> {
|
||||
let mut files = self.files.lock();
|
||||
if i.into() < files.len() {
|
||||
files[i.into()].take()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
112
kernel/src/context/event.rs
Normal file
112
kernel/src/context/event.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use alloc::arc::{Arc, Weak};
|
||||
use collections::BTreeMap;
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use context;
|
||||
use scheme::{FileHandle, SchemeId};
|
||||
use sync::WaitQueue;
|
||||
use syscall::data::Event;
|
||||
|
||||
type EventList = Weak<WaitQueue<Event>>;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct RegKey {
|
||||
scheme_id: SchemeId,
|
||||
event_id: usize,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ProcessKey {
|
||||
context_id: context::context::ContextId,
|
||||
fd: FileHandle,
|
||||
}
|
||||
|
||||
type Registry = BTreeMap<RegKey, BTreeMap<ProcessKey, EventList>>;
|
||||
|
||||
static REGISTRY: Once<RwLock<Registry>> = Once::new();
|
||||
|
||||
/// Initialize registry, called if needed
|
||||
fn init_registry() -> RwLock<Registry> {
|
||||
RwLock::new(Registry::new())
|
||||
}
|
||||
|
||||
/// Get the global schemes list, const
|
||||
fn registry() -> RwLockReadGuard<'static, Registry> {
|
||||
REGISTRY.call_once(init_registry).read()
|
||||
}
|
||||
|
||||
/// Get the global schemes list, mutable
|
||||
pub fn registry_mut() -> RwLockWriteGuard<'static, Registry> {
|
||||
REGISTRY.call_once(init_registry).write()
|
||||
}
|
||||
|
||||
pub fn register(fd: FileHandle, scheme_id: SchemeId, event_id: usize) -> bool {
|
||||
let (context_id, events) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().expect("event::register: No context");
|
||||
let context = context_lock.read();
|
||||
(context.id, Arc::downgrade(&context.events))
|
||||
};
|
||||
|
||||
let mut registry = registry_mut();
|
||||
let entry = registry.entry(RegKey {
|
||||
scheme_id: scheme_id,
|
||||
event_id: event_id
|
||||
}).or_insert_with(|| {
|
||||
BTreeMap::new()
|
||||
});
|
||||
let process_key = ProcessKey {
|
||||
context_id: context_id,
|
||||
fd: fd
|
||||
};
|
||||
if entry.contains_key(&process_key) {
|
||||
false
|
||||
} else {
|
||||
entry.insert(process_key, events);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unregister(fd: FileHandle, scheme_id: SchemeId, event_id: usize) {
|
||||
let mut registry = registry_mut();
|
||||
|
||||
let mut remove = false;
|
||||
let key = RegKey {
|
||||
scheme_id: scheme_id,
|
||||
event_id: event_id
|
||||
};
|
||||
if let Some(entry) = registry.get_mut(&key) {
|
||||
let process_key = ProcessKey {
|
||||
context_id: context::context_id(),
|
||||
fd: fd,
|
||||
};
|
||||
entry.remove(&process_key);
|
||||
|
||||
if entry.is_empty() {
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
|
||||
if remove {
|
||||
registry.remove(&key);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trigger(scheme_id: SchemeId, event_id: usize, flags: usize, data: usize) {
|
||||
let registry = registry();
|
||||
let key = RegKey {
|
||||
scheme_id: scheme_id,
|
||||
event_id: event_id
|
||||
};
|
||||
if let Some(event_lists) = registry.get(&key) {
|
||||
for entry in event_lists.iter() {
|
||||
if let Some(event_list) = entry.1.upgrade() {
|
||||
event_list.send(Event {
|
||||
id: (entry.0).fd.into(),
|
||||
flags: flags,
|
||||
data: data
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
kernel/src/context/file.rs
Normal file
15
kernel/src/context/file.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
//! File struct
|
||||
|
||||
use scheme::SchemeId;
|
||||
|
||||
/// A file
|
||||
//TODO: Close on exec
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct File {
|
||||
/// The scheme that this file refers to
|
||||
pub scheme: SchemeId,
|
||||
/// The number the scheme uses to refer to this file
|
||||
pub number: usize,
|
||||
/// If events are on, this is the event ID
|
||||
pub event: Option<usize>,
|
||||
}
|
91
kernel/src/context/list.rs
Normal file
91
kernel/src/context/list.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use alloc::arc::Arc;
|
||||
use alloc::boxed::Box;
|
||||
use collections::BTreeMap;
|
||||
use core::mem;
|
||||
use core::sync::atomic::Ordering;
|
||||
use spin::RwLock;
|
||||
|
||||
use arch;
|
||||
use syscall::error::{Result, Error, EAGAIN};
|
||||
use super::context::{Context, ContextId};
|
||||
|
||||
/// Context list type
|
||||
pub struct ContextList {
|
||||
map: BTreeMap<ContextId, Arc<RwLock<Context>>>,
|
||||
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: ContextId) -> Option<&Arc<RwLock<Context>>> {
|
||||
self.map.get(&id)
|
||||
}
|
||||
|
||||
/// Get the current context.
|
||||
pub fn current(&self) -> Option<&Arc<RwLock<Context>>> {
|
||||
self.map.get(&super::CONTEXT_ID.load(Ordering::SeqCst))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> ::collections::btree_map::Iter<ContextId, Arc<RwLock<Context>>> {
|
||||
self.map.iter()
|
||||
}
|
||||
|
||||
/// Create a new context.
|
||||
pub fn new_context(&mut self) -> Result<&Arc<RwLock<Context>>> {
|
||||
if self.next_id >= super::CONTEXT_MAX_CONTEXTS {
|
||||
self.next_id = 1;
|
||||
}
|
||||
|
||||
while self.map.contains_key(&ContextId::from(self.next_id)) {
|
||||
self.next_id += 1;
|
||||
}
|
||||
|
||||
if self.next_id >= super::CONTEXT_MAX_CONTEXTS {
|
||||
return Err(Error::new(EAGAIN));
|
||||
}
|
||||
|
||||
let id = ContextId::from(self.next_id);
|
||||
self.next_id += 1;
|
||||
|
||||
assert!(self.map.insert(id, Arc::new(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<&Arc<RwLock<Context>>> {
|
||||
let context_lock = self.new_context()?;
|
||||
{
|
||||
let mut context = context_lock.write();
|
||||
let mut fx = unsafe { Box::from_raw(::alloc::heap::allocate(512, 16) as *mut [u8; 512]) };
|
||||
for b in fx.iter_mut() {
|
||||
*b = 0;
|
||||
}
|
||||
let mut stack = vec![0; 65536].into_boxed_slice();
|
||||
let offset = stack.len() - mem::size_of::<usize>();
|
||||
unsafe {
|
||||
let offset = stack.len() - mem::size_of::<usize>();
|
||||
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_fx(fx.as_ptr() as usize);
|
||||
context.arch.set_stack(stack.as_ptr() as usize + offset);
|
||||
context.kfx = Some(fx);
|
||||
context.kstack = Some(stack);
|
||||
}
|
||||
Ok(context_lock)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: ContextId) -> Option<Arc<RwLock<Context>>> {
|
||||
self.map.remove(&id)
|
||||
}
|
||||
}
|
327
kernel/src/context/memory.rs
Normal file
327
kernel/src/context/memory.rs
Normal file
|
@ -0,0 +1,327 @@
|
|||
use alloc::arc::{Arc, Weak};
|
||||
use collections::VecDeque;
|
||||
use core::intrinsics;
|
||||
use spin::Mutex;
|
||||
|
||||
use arch::memory::Frame;
|
||||
use arch::paging::{ActivePageTable, InactivePageTable, Page, PageIter, PhysicalAddress, VirtualAddress};
|
||||
use arch::paging::entry::{self, EntryFlags};
|
||||
use arch::paging::mapper::MapperFlushAll;
|
||||
use arch::paging::temporary_page::TemporaryPage;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Grant {
|
||||
start: VirtualAddress,
|
||||
size: usize,
|
||||
flags: EntryFlags,
|
||||
mapped: bool
|
||||
}
|
||||
|
||||
impl Grant {
|
||||
pub fn physmap(from: PhysicalAddress, to: VirtualAddress, size: usize, flags: EntryFlags) -> Grant {
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let mut flush_all = MapperFlushAll::new();
|
||||
|
||||
let start_page = Page::containing_address(to);
|
||||
let end_page = Page::containing_address(VirtualAddress::new(to.get() + size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - to.get() + from.get()));
|
||||
let result = active_table.map_to(page, frame, flags);
|
||||
flush_all.consume(result);
|
||||
}
|
||||
|
||||
flush_all.flush(&mut active_table);
|
||||
|
||||
Grant {
|
||||
start: to,
|
||||
size: size,
|
||||
flags: flags,
|
||||
mapped: true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_inactive(from: VirtualAddress, to: VirtualAddress, size: usize, flags: EntryFlags, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) -> Grant {
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let mut frames = VecDeque::new();
|
||||
|
||||
let start_page = Page::containing_address(from);
|
||||
let end_page = Page::containing_address(VirtualAddress::new(from.get() + size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
let frame = active_table.translate_page(page).expect("grant references unmapped memory");
|
||||
frames.push_back(frame);
|
||||
}
|
||||
|
||||
active_table.with(new_table, temporary_page, |mapper| {
|
||||
let start_page = Page::containing_address(to);
|
||||
let end_page = Page::containing_address(VirtualAddress::new(to.get() + size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
let frame = frames.pop_front().expect("grant did not find enough frames");
|
||||
let result = mapper.map_to(page, frame, flags);
|
||||
// Ignore result due to mapping on inactive table
|
||||
unsafe { result.ignore(); }
|
||||
}
|
||||
});
|
||||
|
||||
Grant {
|
||||
start: to,
|
||||
size: size,
|
||||
flags: flags,
|
||||
mapped: true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_address(&self) -> VirtualAddress {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> EntryFlags {
|
||||
self.flags
|
||||
}
|
||||
|
||||
pub fn unmap(mut self) {
|
||||
assert!(self.mapped);
|
||||
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let mut flush_all = MapperFlushAll::new();
|
||||
|
||||
let start_page = Page::containing_address(self.start);
|
||||
let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
let (result, _frame) = active_table.unmap_return(page);
|
||||
flush_all.consume(result);
|
||||
}
|
||||
|
||||
flush_all.flush(&mut active_table);
|
||||
|
||||
self.mapped = false;
|
||||
}
|
||||
|
||||
pub fn unmap_inactive(mut self, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) {
|
||||
assert!(self.mapped);
|
||||
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
active_table.with(new_table, temporary_page, |mapper| {
|
||||
let start_page = Page::containing_address(self.start);
|
||||
let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
let (result, _frame) = mapper.unmap_return(page);
|
||||
// This is not the active table, so the flush can be ignored
|
||||
unsafe { result.ignore(); }
|
||||
}
|
||||
});
|
||||
|
||||
self.mapped = false;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Grant {
|
||||
fn drop(&mut self) {
|
||||
assert!(!self.mapped);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SharedMemory {
|
||||
Owned(Arc<Mutex<Memory>>),
|
||||
Borrowed(Weak<Mutex<Memory>>)
|
||||
}
|
||||
|
||||
impl SharedMemory {
|
||||
pub fn with<F, T>(&self, f: F) -> T where F: FnOnce(&mut Memory) -> T {
|
||||
match *self {
|
||||
SharedMemory::Owned(ref memory_lock) => {
|
||||
let mut memory = memory_lock.lock();
|
||||
f(&mut *memory)
|
||||
},
|
||||
SharedMemory::Borrowed(ref memory_weak) => {
|
||||
let memory_lock = memory_weak.upgrade().expect("SharedMemory::Borrowed no longer valid");
|
||||
let mut memory = memory_lock.lock();
|
||||
f(&mut *memory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> SharedMemory {
|
||||
match *self {
|
||||
SharedMemory::Owned(ref memory_lock) => SharedMemory::Borrowed(Arc::downgrade(memory_lock)),
|
||||
SharedMemory::Borrowed(ref memory_lock) => SharedMemory::Borrowed(memory_lock.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Memory {
|
||||
start: VirtualAddress,
|
||||
size: usize,
|
||||
flags: EntryFlags
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new(start: VirtualAddress, size: usize, flags: EntryFlags, clear: bool) -> Self {
|
||||
let mut memory = Memory {
|
||||
start: start,
|
||||
size: size,
|
||||
flags: flags
|
||||
};
|
||||
|
||||
memory.map(clear);
|
||||
|
||||
memory
|
||||
}
|
||||
|
||||
pub fn to_shared(self) -> SharedMemory {
|
||||
SharedMemory::Owned(Arc::new(Mutex::new(self)))
|
||||
}
|
||||
|
||||
pub fn start_address(&self) -> VirtualAddress {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> EntryFlags {
|
||||
self.flags
|
||||
}
|
||||
|
||||
pub fn pages(&self) -> PageIter {
|
||||
let start_page = Page::containing_address(self.start);
|
||||
let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
|
||||
Page::range_inclusive(start_page, end_page)
|
||||
}
|
||||
|
||||
fn map(&mut self, clear: bool) {
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let mut flush_all = MapperFlushAll::new();
|
||||
|
||||
for page in self.pages() {
|
||||
let result = active_table.map(page, self.flags);
|
||||
flush_all.consume(result);
|
||||
}
|
||||
|
||||
flush_all.flush(&mut active_table);
|
||||
|
||||
if clear {
|
||||
assert!(self.flags.contains(entry::WRITABLE));
|
||||
unsafe {
|
||||
intrinsics::write_bytes(self.start_address().get() as *mut u8, 0, self.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unmap(&mut self) {
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let mut flush_all = MapperFlushAll::new();
|
||||
|
||||
for page in self.pages() {
|
||||
let result = active_table.unmap(page);
|
||||
flush_all.consume(result);
|
||||
}
|
||||
|
||||
flush_all.flush(&mut active_table);
|
||||
}
|
||||
|
||||
/// A complicated operation to move a piece of memory to a new page table
|
||||
/// It also allows for changing the address at the same time
|
||||
pub fn move_to(&mut self, new_start: VirtualAddress, new_table: &mut InactivePageTable, temporary_page: &mut TemporaryPage) {
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let mut flush_all = MapperFlushAll::new();
|
||||
|
||||
for page in self.pages() {
|
||||
let (result, frame) = active_table.unmap_return(page);
|
||||
flush_all.consume(result);
|
||||
|
||||
active_table.with(new_table, temporary_page, |mapper| {
|
||||
let new_page = Page::containing_address(VirtualAddress::new(page.start_address().get() - self.start.get() + new_start.get()));
|
||||
let result = mapper.map_to(new_page, frame, self.flags);
|
||||
// This is not the active table, so the flush can be ignored
|
||||
unsafe { result.ignore(); }
|
||||
});
|
||||
}
|
||||
|
||||
flush_all.flush(&mut active_table);
|
||||
|
||||
self.start = new_start;
|
||||
}
|
||||
|
||||
pub fn remap(&mut self, new_flags: EntryFlags) {
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let mut flush_all = MapperFlushAll::new();
|
||||
|
||||
for page in self.pages() {
|
||||
let result = active_table.remap(page, new_flags);
|
||||
flush_all.consume(result);
|
||||
}
|
||||
|
||||
flush_all.flush(&mut active_table);
|
||||
|
||||
self.flags = new_flags;
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: usize, clear: bool) {
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
//TODO: Calculate page changes to minimize operations
|
||||
if new_size > self.size {
|
||||
let mut flush_all = MapperFlushAll::new();
|
||||
|
||||
let start_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size));
|
||||
let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + new_size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
if active_table.translate_page(page).is_none() {
|
||||
let result = active_table.map(page, self.flags);
|
||||
flush_all.consume(result);
|
||||
}
|
||||
}
|
||||
|
||||
flush_all.flush(&mut active_table);
|
||||
|
||||
if clear {
|
||||
unsafe {
|
||||
intrinsics::write_bytes((self.start.get() + self.size) as *mut u8, 0, new_size - self.size);
|
||||
}
|
||||
}
|
||||
} else if new_size < self.size {
|
||||
let mut flush_all = MapperFlushAll::new();
|
||||
|
||||
let start_page = Page::containing_address(VirtualAddress::new(self.start.get() + new_size));
|
||||
let end_page = Page::containing_address(VirtualAddress::new(self.start.get() + self.size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
if active_table.translate_page(page).is_some() {
|
||||
let result = active_table.unmap(page);
|
||||
flush_all.consume(result);
|
||||
}
|
||||
}
|
||||
|
||||
flush_all.flush(&mut active_table);
|
||||
}
|
||||
|
||||
self.size = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Memory {
|
||||
fn drop(&mut self) {
|
||||
self.unmap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tls {
|
||||
pub master: VirtualAddress,
|
||||
pub file_size: usize,
|
||||
pub mem: Memory
|
||||
}
|
75
kernel/src/context/mod.rs
Normal file
75
kernel/src/context/mod.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
//! Context management
|
||||
use alloc::boxed::Box;
|
||||
use core::sync::atomic::Ordering;
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
pub use self::context::{Context, Status};
|
||||
pub use self::list::ContextList;
|
||||
pub use self::switch::switch;
|
||||
pub use context::context::ContextId;
|
||||
|
||||
/// Context struct
|
||||
mod context;
|
||||
|
||||
/// Context list
|
||||
mod list;
|
||||
|
||||
/// Context switch function
|
||||
mod switch;
|
||||
|
||||
/// Event handling
|
||||
pub mod event;
|
||||
|
||||
/// File struct - defines a scheme and a file number
|
||||
pub mod file;
|
||||
|
||||
/// Memory struct - contains a set of pages for a context
|
||||
pub mod memory;
|
||||
|
||||
/// Limit on number of contexts
|
||||
pub const CONTEXT_MAX_CONTEXTS: usize = usize::max_value() - 1;
|
||||
|
||||
/// Maximum context files
|
||||
pub const CONTEXT_MAX_FILES: usize = 65536;
|
||||
|
||||
/// Contexts list
|
||||
static CONTEXTS: Once<RwLock<ContextList>> = Once::new();
|
||||
|
||||
#[thread_local]
|
||||
static CONTEXT_ID: context::AtomicContextId = context::AtomicContextId::default();
|
||||
|
||||
pub fn init() {
|
||||
let mut contexts = contexts_mut();
|
||||
let context_lock = contexts.new_context().expect("could not initialize first context");
|
||||
let mut context = context_lock.write();
|
||||
let mut fx = unsafe { Box::from_raw(::alloc::heap::allocate(512, 16) as *mut [u8; 512]) };
|
||||
for b in fx.iter_mut() {
|
||||
*b = 0;
|
||||
}
|
||||
|
||||
context.arch.set_fx(fx.as_ptr() as usize);
|
||||
context.kfx = Some(fx);
|
||||
context.status = Status::Runnable;
|
||||
context.running = true;
|
||||
context.cpu_id = Some(::cpu_id());
|
||||
CONTEXT_ID.store(context.id, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Initialize contexts, called if needed
|
||||
fn init_contexts() -> RwLock<ContextList> {
|
||||
RwLock::new(ContextList::new())
|
||||
}
|
||||
|
||||
/// Get the global schemes list, const
|
||||
pub fn contexts() -> RwLockReadGuard<'static, ContextList> {
|
||||
CONTEXTS.call_once(init_contexts).read()
|
||||
}
|
||||
|
||||
/// Get the global schemes list, mutable
|
||||
pub fn contexts_mut() -> RwLockWriteGuard<'static, ContextList> {
|
||||
CONTEXTS.call_once(init_contexts).write()
|
||||
}
|
||||
|
||||
pub fn context_id() -> context::ContextId {
|
||||
CONTEXT_ID.load(Ordering::SeqCst)
|
||||
}
|
115
kernel/src/context/switch.rs
Normal file
115
kernel/src/context/switch.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
use core::sync::atomic::Ordering;
|
||||
|
||||
use arch;
|
||||
use context::{contexts, Context, Status, CONTEXT_ID};
|
||||
use syscall;
|
||||
|
||||
/// Switch to the next context
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Do not call this while holding locks!
|
||||
pub unsafe fn switch() -> bool {
|
||||
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 cpu_id = ::cpu_id();
|
||||
|
||||
let from_ptr;
|
||||
let mut to_ptr = 0 as *mut Context;
|
||||
let mut to_sig = None;
|
||||
{
|
||||
let contexts = contexts();
|
||||
{
|
||||
let context_lock = contexts.current().expect("context::switch: not inside of context");
|
||||
let mut context = context_lock.write();
|
||||
from_ptr = context.deref_mut() as *mut Context;
|
||||
}
|
||||
|
||||
let check_context = |context: &mut Context| -> bool {
|
||||
if context.cpu_id == None && cpu_id == 0 {
|
||||
context.cpu_id = Some(cpu_id);
|
||||
// println!("{}: take {} {}", cpu_id, context.id, ::core::str::from_utf8_unchecked(&context.name.lock()));
|
||||
}
|
||||
|
||||
if context.status == Status::Blocked && ! context.pending.is_empty() {
|
||||
context.unblock();
|
||||
}
|
||||
|
||||
if context.status == Status::Blocked && context.wake.is_some() {
|
||||
let wake = context.wake.expect("context::switch: wake not set");
|
||||
|
||||
let current = arch::time::monotonic();
|
||||
if current.0 > wake.0 || (current.0 == wake.0 && current.1 >= wake.1) {
|
||||
context.unblock();
|
||||
}
|
||||
}
|
||||
|
||||
if context.cpu_id == Some(cpu_id) {
|
||||
if context.status == Status::Runnable && ! context.running {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
};
|
||||
|
||||
for (pid, context_lock) in contexts.iter() {
|
||||
if *pid > (*from_ptr).id {
|
||||
let mut context = context_lock.write();
|
||||
if check_context(&mut context) {
|
||||
to_ptr = context.deref_mut() as *mut Context;
|
||||
to_sig = context.pending.pop_front();
|
||||
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 check_context(&mut context) {
|
||||
to_ptr = context.deref_mut() as *mut Context;
|
||||
to_sig = context.pending.pop_front();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if to_ptr as usize == 0 {
|
||||
// Unset global lock if no context found
|
||||
arch::context::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
|
||||
return false;
|
||||
}
|
||||
|
||||
(&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);
|
||||
|
||||
// Unset global lock before switch, as arch is only usable by the current CPU at this time
|
||||
arch::context::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
|
||||
|
||||
if let Some(sig) = to_sig {
|
||||
println!("Handle {}", sig);
|
||||
(&mut *to_ptr).arch.signal_stack(signal_handler, sig);
|
||||
}
|
||||
|
||||
(&mut *from_ptr).arch.switch_to(&mut (&mut *to_ptr).arch);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
extern fn signal_handler(signal: usize) {
|
||||
println!("Signal handler: {}", signal);
|
||||
syscall::exit(signal);
|
||||
}
|
73
kernel/src/elf.rs
Normal file
73
kernel/src/elf.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
//! ELF executables
|
||||
|
||||
use collections::String;
|
||||
|
||||
use core::str;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub use goblin::elf32::{header, program_header};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use goblin::elf64::{header, program_header};
|
||||
|
||||
/// An ELF executable
|
||||
pub struct Elf<'a> {
|
||||
pub data: &'a [u8],
|
||||
header: &'a header::Header
|
||||
}
|
||||
|
||||
impl<'a> Elf<'a> {
|
||||
/// Create a ELF executable from data
|
||||
pub fn from(data: &'a [u8]) -> Result<Elf<'a>, String> {
|
||||
if data.len() < header::SIZEOF_EHDR {
|
||||
Err(format!("Elf: Not enough data: {} < {}", data.len(), header::SIZEOF_EHDR))
|
||||
} else if &data[..header::SELFMAG] != header::ELFMAG {
|
||||
Err(format!("Elf: Invalid magic: {:?} != {:?}", &data[..4], header::ELFMAG))
|
||||
} else if data.get(header::EI_CLASS) != Some(&header::ELFCLASS) {
|
||||
Err(format!("Elf: Invalid architecture: {:?} != {:?}", data.get(header::EI_CLASS), header::ELFCLASS))
|
||||
} else {
|
||||
Ok(Elf {
|
||||
data: data,
|
||||
header: unsafe { &*(data.as_ptr() as usize as *const header::Header) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn segments(&'a self) -> ElfSegments<'a> {
|
||||
ElfSegments {
|
||||
data: self.data,
|
||||
header: self.header,
|
||||
i: 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the entry field of the header
|
||||
pub fn entry(&self) -> usize {
|
||||
self.header.e_entry as usize
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ElfSegments<'a> {
|
||||
data: &'a [u8],
|
||||
header: &'a header::Header,
|
||||
i: usize
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ElfSegments<'a> {
|
||||
type Item = &'a program_header::ProgramHeader;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.i < self.header.e_phnum as usize {
|
||||
let item = unsafe {
|
||||
&* ((
|
||||
self.data.as_ptr() as usize
|
||||
+ self.header.e_phoff as usize
|
||||
+ self.i * self.header.e_phentsize as usize
|
||||
) as *const program_header::ProgramHeader)
|
||||
};
|
||||
self.i += 1;
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
180
kernel/src/lib.rs
Normal file
180
kernel/src/lib.rs
Normal file
|
@ -0,0 +1,180 @@
|
|||
//! # The Redox OS Kernel, version 2
|
||||
//!
|
||||
//! The Redox OS Kernel is a hybrid kernel that supports X86_64 systems and
|
||||
//! provides Unix-like syscalls for primarily Rust applications
|
||||
|
||||
#![deny(warnings)]
|
||||
#![feature(alloc)]
|
||||
#![feature(asm)]
|
||||
#![feature(collections)]
|
||||
#![feature(const_fn)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(drop_types_in_const)]
|
||||
#![feature(heap_api)]
|
||||
#![feature(integer_atomics)]
|
||||
#![feature(never_type)]
|
||||
#![feature(thread_local)]
|
||||
#![no_std]
|
||||
|
||||
use arch::interrupt;
|
||||
|
||||
/// Architecture specific items (test)
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate arch_test as arch;
|
||||
|
||||
/// Architecture specific items (ARM)
|
||||
#[cfg(all(not(test), target_arch = "arm"))]
|
||||
#[macro_use]
|
||||
extern crate arch_arm as arch;
|
||||
|
||||
/// Architecture specific items (x86_64)
|
||||
#[cfg(all(not(test), target_arch = "x86_64"))]
|
||||
#[macro_use]
|
||||
extern crate arch_x86_64 as arch;
|
||||
|
||||
extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate collections;
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate goblin;
|
||||
extern crate spin;
|
||||
|
||||
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
use scheme::FileHandle;
|
||||
|
||||
#[macro_use]
|
||||
/// Shared data structures
|
||||
pub mod common;
|
||||
|
||||
/// Context management
|
||||
pub mod context;
|
||||
|
||||
/// ELF file parsing
|
||||
pub mod elf;
|
||||
|
||||
/// Schemes, filesystem handlers
|
||||
pub mod scheme;
|
||||
|
||||
/// Synchronization primitives
|
||||
pub mod sync;
|
||||
|
||||
/// Syscall handlers
|
||||
pub mod syscall;
|
||||
|
||||
/// Tests
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
||||
/// A unique number that identifies the current CPU - used for scheduling
|
||||
#[thread_local]
|
||||
static CPU_ID: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
/// Get the current CPU's scheduling ID
|
||||
#[inline(always)]
|
||||
pub fn cpu_id() -> usize {
|
||||
CPU_ID.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// The count of all CPUs that can have work scheduled
|
||||
static CPU_COUNT : AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
/// Get the number of CPUs currently active
|
||||
#[inline(always)]
|
||||
pub fn cpu_count() -> usize {
|
||||
CPU_COUNT.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Initialize userspace by running the initfs:bin/init process
|
||||
/// This function will also set the CWD to initfs:bin and open debug: as stdio
|
||||
pub extern fn userspace_init() {
|
||||
assert_eq!(syscall::chdir(b"initfs:"), Ok(0));
|
||||
|
||||
assert_eq!(syscall::open(b"debug:", syscall::flag::O_RDONLY).map(FileHandle::into), Ok(0));
|
||||
assert_eq!(syscall::open(b"debug:", syscall::flag::O_WRONLY).map(FileHandle::into), Ok(1));
|
||||
assert_eq!(syscall::open(b"debug:", syscall::flag::O_WRONLY).map(FileHandle::into), Ok(2));
|
||||
|
||||
syscall::exec(b"/bin/init", &[]).expect("failed to execute init");
|
||||
|
||||
panic!("init returned");
|
||||
}
|
||||
|
||||
/// Allow exception handlers to send signal to arch-independant kernel
|
||||
#[no_mangle]
|
||||
pub extern fn ksignal(signal: usize) {
|
||||
println!("SIGNAL {}, CPU {}, PID {:?}", signal, cpu_id(), context::context_id());
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context_lock) = contexts.current() {
|
||||
let context = context_lock.read();
|
||||
println!("NAME {}", unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) });
|
||||
}
|
||||
}
|
||||
syscall::exit(signal & 0x7F);
|
||||
}
|
||||
|
||||
/// This is the kernel entry point for the primary CPU. The arch crate is responsible for calling this
|
||||
#[no_mangle]
|
||||
pub extern fn kmain(cpus: usize) {
|
||||
CPU_ID.store(0, Ordering::SeqCst);
|
||||
CPU_COUNT.store(cpus, Ordering::SeqCst);
|
||||
|
||||
context::init();
|
||||
|
||||
let pid = syscall::getpid();
|
||||
println!("BSP: {:?} {}", pid, cpus);
|
||||
|
||||
match context::contexts_mut().spawn(userspace_init) {
|
||||
Ok(context_lock) => {
|
||||
let mut context = context_lock.write();
|
||||
context.status = context::Status::Runnable;
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("failed to spawn userspace_init: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
interrupt::disable();
|
||||
if context::switch() {
|
||||
interrupt::enable_and_nop();
|
||||
} else {
|
||||
// Enable interrupts, then halt CPU (to save power) until the next interrupt is actually fired.
|
||||
interrupt::enable_and_halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the main kernel entry point for secondary CPUs
|
||||
#[no_mangle]
|
||||
pub extern fn kmain_ap(_id: usize) {
|
||||
// Disable APs for now
|
||||
loop {
|
||||
unsafe { interrupt::enable_and_halt(); }
|
||||
}
|
||||
|
||||
/*
|
||||
CPU_ID.store(id, Ordering::SeqCst);
|
||||
|
||||
context::init();
|
||||
|
||||
let pid = syscall::getpid();
|
||||
println!("AP {}: {:?}", id, pid);
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
interrupt::disable();
|
||||
if context::switch() {
|
||||
interrupt::enable_and_nop();
|
||||
} else {
|
||||
// Enable interrupts, then halt CPU (to save power) until the next interrupt is actually fired.
|
||||
interrupt::enable_and_halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
75
kernel/src/scheme/debug.rs
Normal file
75
kernel/src/scheme/debug.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
use core::str;
|
||||
use core::sync::atomic::Ordering;
|
||||
use spin::Once;
|
||||
|
||||
use context;
|
||||
use scheme::*;
|
||||
use sync::WaitQueue;
|
||||
use syscall::flag::EVENT_READ;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub static DEBUG_SCHEME_ID: AtomicSchemeId = ATOMIC_SCHEMEID_INIT;
|
||||
|
||||
/// Input queue
|
||||
static INPUT: Once<WaitQueue<u8>> = Once::new();
|
||||
|
||||
/// Initialize input queue, called if needed
|
||||
fn init_input() -> WaitQueue<u8> {
|
||||
WaitQueue::new()
|
||||
}
|
||||
|
||||
/// Add to the input queue
|
||||
#[no_mangle]
|
||||
pub extern fn debug_input(b: u8) {
|
||||
let len = INPUT.call_once(init_input).send(b);
|
||||
|
||||
context::event::trigger(DEBUG_SCHEME_ID.load(Ordering::SeqCst), 0, EVENT_READ, len);
|
||||
}
|
||||
|
||||
pub struct DebugScheme;
|
||||
|
||||
impl DebugScheme {
|
||||
pub fn new(scheme_id: SchemeId) -> DebugScheme {
|
||||
DEBUG_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
|
||||
DebugScheme
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for DebugScheme {
|
||||
fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn dup(&self, _file: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Read the file `number` into the `buffer`
|
||||
///
|
||||
/// Returns the number of bytes read
|
||||
fn read(&self, _file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Ok(INPUT.call_once(init_input).receive_into(buf, true))
|
||||
}
|
||||
|
||||
/// Write the `buffer` to the `file`
|
||||
///
|
||||
/// Returns the number of bytes written
|
||||
fn write(&self, _file: usize, buffer: &[u8]) -> Result<usize> {
|
||||
//TODO: Write bytes, do not convert to str
|
||||
print!("{}", unsafe { str::from_utf8_unchecked(buffer) });
|
||||
Ok(buffer.len())
|
||||
}
|
||||
|
||||
fn fevent(&self, _file: usize, _flags: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Close the file `number`
|
||||
fn close(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
193
kernel/src/scheme/env.rs
Normal file
193
kernel/src/scheme/env.rs
Normal file
|
@ -0,0 +1,193 @@
|
|||
use alloc::arc::Arc;
|
||||
use collections::{BTreeMap, Vec};
|
||||
use core::{cmp, str};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::{Mutex, RwLock};
|
||||
|
||||
use context;
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{MODE_FILE, SEEK_SET, SEEK_CUR, SEEK_END};
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Handle {
|
||||
data: Arc<Mutex<Vec<u8>>>,
|
||||
mode: u16,
|
||||
seek: usize
|
||||
}
|
||||
|
||||
pub struct EnvScheme {
|
||||
next_id: AtomicUsize,
|
||||
handles: RwLock<BTreeMap<usize, Handle>>
|
||||
}
|
||||
|
||||
impl EnvScheme {
|
||||
pub fn new() -> EnvScheme {
|
||||
EnvScheme {
|
||||
next_id: AtomicUsize::new(0),
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for EnvScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let path = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?.trim_matches('/');
|
||||
|
||||
let env_lock = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.env.clone()
|
||||
};
|
||||
|
||||
if path.is_empty() {
|
||||
let mut list = Vec::new();
|
||||
{
|
||||
let env = env_lock.lock();
|
||||
for entry in env.iter() {
|
||||
if ! list.is_empty() {
|
||||
list.push(b'\n');
|
||||
}
|
||||
list.extend_from_slice(&entry.0);
|
||||
list.push(b'=');
|
||||
list.extend_from_slice(&entry.1.lock());
|
||||
}
|
||||
}
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
data: Arc::new(Mutex::new(list)),
|
||||
mode: MODE_FILE,
|
||||
seek: 0
|
||||
});
|
||||
|
||||
Ok(id)
|
||||
} else {
|
||||
let data = {
|
||||
let mut env = env_lock.lock();
|
||||
if env.contains_key(path.as_bytes()) {
|
||||
env[path.as_bytes()].clone()
|
||||
} else /*if flags & O_CREAT == O_CREAT*/ {
|
||||
let name = path.as_bytes().to_vec().into_boxed_slice();
|
||||
let data = Arc::new(Mutex::new(Vec::new()));
|
||||
env.insert(name, data.clone());
|
||||
data
|
||||
}
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
data: data,
|
||||
mode: MODE_FILE,
|
||||
seek: 0
|
||||
});
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let new_handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, new_handle);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let data = handle.data.lock();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buffer.len() && handle.seek < data.len() {
|
||||
buffer[i] = data[handle.seek];
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buffer: &[u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut data = handle.data.lock();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buffer.len() && handle.seek < data.len() {
|
||||
data[handle.seek] = buffer[i];
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
|
||||
while i < buffer.len() {
|
||||
data.push(buffer[i]);
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let len = handle.data.lock().len();
|
||||
handle.seek = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len as isize, handle.seek as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(handle.seek)
|
||||
}
|
||||
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
stat.st_mode = handle.mode;
|
||||
stat.st_size = handle.data.lock().len() as u64;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let _handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn ftruncate(&self, id: usize, len: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut data = handle.data.lock();
|
||||
if len < data.len() {
|
||||
data.truncate(len)
|
||||
} else {
|
||||
while len > data.len() {
|
||||
data.push(0);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
74
kernel/src/scheme/event.rs
Normal file
74
kernel/src/scheme/event.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use alloc::arc::{Arc, Weak};
|
||||
use collections::BTreeMap;
|
||||
use core::{mem, slice};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
use context;
|
||||
use sync::WaitQueue;
|
||||
use syscall::data::Event;
|
||||
use syscall::error::*;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub struct EventScheme {
|
||||
next_id: AtomicUsize,
|
||||
handles: RwLock<BTreeMap<usize, Weak<WaitQueue<Event>>>>
|
||||
}
|
||||
|
||||
impl EventScheme {
|
||||
pub fn new() -> EventScheme {
|
||||
EventScheme {
|
||||
next_id: AtomicUsize::new(0),
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for EventScheme {
|
||||
fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let handle = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.events.clone()
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Arc::downgrade(&handle));
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle_weak.upgrade().ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let new_id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(new_id, Arc::downgrade(&handle));
|
||||
Ok(new_id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle_weak.upgrade().ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let event_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Event, buf.len()/mem::size_of::<Event>()) };
|
||||
Ok(handle.receive_into(event_buf, true) * mem::size_of::<Event>())
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle_weak = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle_weak.upgrade().ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
154
kernel/src/scheme/initfs.rs
Normal file
154
kernel/src/scheme/initfs.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use collections::BTreeMap;
|
||||
use core::{cmp, str};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{MODE_DIR, MODE_FILE, SEEK_SET, SEEK_CUR, SEEK_END};
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
#[cfg(test)]
|
||||
mod gen {
|
||||
use collections::BTreeMap;
|
||||
pub fn gen() -> BTreeMap<&'static [u8], (&'static [u8], bool)> { BTreeMap::new() }
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[path="../../../build/userspace/initfs.rs"]
|
||||
mod gen;
|
||||
|
||||
struct Handle {
|
||||
path: &'static [u8],
|
||||
data: &'static [u8],
|
||||
mode: u16,
|
||||
seek: usize
|
||||
}
|
||||
|
||||
pub struct InitFsScheme {
|
||||
next_id: AtomicUsize,
|
||||
files: BTreeMap<&'static [u8], (&'static [u8], bool)>,
|
||||
handles: RwLock<BTreeMap<usize, Handle>>
|
||||
}
|
||||
|
||||
impl InitFsScheme {
|
||||
pub fn new() -> InitFsScheme {
|
||||
InitFsScheme {
|
||||
next_id: AtomicUsize::new(0),
|
||||
files: gen::gen(),
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for InitFsScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let path_utf8 = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?;
|
||||
let path_trimmed = path_utf8.trim_matches('/');
|
||||
|
||||
//Have to iterate to get the path without allocation
|
||||
for entry in self.files.iter() {
|
||||
if entry.0 == &path_trimmed.as_bytes() {
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
path: entry.0,
|
||||
data: (entry.1).0,
|
||||
mode: if (entry.1).1 { MODE_DIR | 0o755 } else { MODE_FILE | 0o744 },
|
||||
seek: 0
|
||||
});
|
||||
|
||||
return Ok(id);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
|
||||
fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let (path, data, mode, seek) = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
(handle.path, handle.data, handle.mode, handle.seek)
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
path: path,
|
||||
data: data,
|
||||
mode: mode,
|
||||
seek: seek
|
||||
});
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
while i < buffer.len() && handle.seek < handle.data.len() {
|
||||
buffer[i] = handle.data[handle.seek];
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
handle.seek = match whence {
|
||||
SEEK_SET => cmp::min(handle.data.len(), pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(handle.data.len() as isize, handle.seek as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(handle.data.len() as isize, handle.data.len() as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(handle.seek)
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
//TODO: Copy scheme part in kernel
|
||||
let mut i = 0;
|
||||
let scheme_path = b"initfs:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < handle.path.len() {
|
||||
buf[i] = handle.path[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
stat.st_mode = handle.mode;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = handle.data.len() as u64;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
101
kernel/src/scheme/irq.rs
Normal file
101
kernel/src/scheme/irq.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use core::{mem, str};
|
||||
use core::sync::atomic::Ordering;
|
||||
use spin::Mutex;
|
||||
|
||||
use arch::interrupt::irq::acknowledge;
|
||||
use context;
|
||||
use scheme::{AtomicSchemeId, ATOMIC_SCHEMEID_INIT, SchemeId};
|
||||
use syscall::error::*;
|
||||
use syscall::flag::EVENT_READ;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub static IRQ_SCHEME_ID: AtomicSchemeId = ATOMIC_SCHEMEID_INIT;
|
||||
|
||||
/// IRQ queues
|
||||
static ACKS: Mutex<[usize; 16]> = Mutex::new([0; 16]);
|
||||
static COUNTS: Mutex<[usize; 16]> = Mutex::new([0; 16]);
|
||||
|
||||
/// Add to the input queue
|
||||
#[no_mangle]
|
||||
pub extern fn irq_trigger(irq: u8) {
|
||||
COUNTS.lock()[irq as usize] += 1;
|
||||
context::event::trigger(IRQ_SCHEME_ID.load(Ordering::SeqCst), irq as usize, EVENT_READ, mem::size_of::<usize>());
|
||||
}
|
||||
|
||||
pub struct IrqScheme;
|
||||
|
||||
impl IrqScheme {
|
||||
pub fn new(scheme_id: SchemeId) -> IrqScheme {
|
||||
IRQ_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
|
||||
IrqScheme
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for IrqScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result<usize> {
|
||||
if uid == 0 {
|
||||
let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
let id = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if id < COUNTS.lock().len() {
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&self, file: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
fn read(&self, file: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
// Ensures that the length of the buffer is larger than the size of a usize
|
||||
if buffer.len() >= mem::size_of::<usize>() {
|
||||
let ack = ACKS.lock()[file];
|
||||
let current = COUNTS.lock()[file];
|
||||
if ack != current {
|
||||
// Safe if the length of the buffer is larger than the size of a usize
|
||||
assert!(buffer.len() >= mem::size_of::<usize>());
|
||||
unsafe { *(buffer.as_mut_ptr() as *mut usize) = current; }
|
||||
Ok(mem::size_of::<usize>())
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, file: usize, buffer: &[u8]) -> Result<usize> {
|
||||
if buffer.len() >= mem::size_of::<usize>() {
|
||||
assert!(buffer.len() >= mem::size_of::<usize>());
|
||||
let ack = unsafe { *(buffer.as_ptr() as *const usize) };
|
||||
let current = COUNTS.lock()[file];
|
||||
if ack == current {
|
||||
ACKS.lock()[file] = ack;
|
||||
unsafe { acknowledge(file); }
|
||||
Ok(mem::size_of::<usize>())
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
fn fevent(&self, file: usize, _flags: usize) -> Result<usize> {
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
fn fsync(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
160
kernel/src/scheme/live.rs
Normal file
160
kernel/src/scheme/live.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
/// Disk scheme replacement when making live disk
|
||||
|
||||
use alloc::arc::Arc;
|
||||
use collections::{BTreeMap, Vec};
|
||||
use core::cmp;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{MODE_FILE, SEEK_SET, SEEK_CUR, SEEK_END};
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
static FILESYSTEM: &'static [u8] = include_bytes!("../../../build/filesystem.bin");
|
||||
|
||||
struct Handle {
|
||||
path: &'static [u8],
|
||||
data: Arc<RwLock<Vec<u8>>>,
|
||||
mode: u16,
|
||||
seek: usize
|
||||
}
|
||||
|
||||
pub struct DiskScheme {
|
||||
next_id: AtomicUsize,
|
||||
data: Arc<RwLock<Vec<u8>>>,
|
||||
handles: RwLock<BTreeMap<usize, Handle>>
|
||||
}
|
||||
|
||||
impl DiskScheme {
|
||||
pub fn new() -> DiskScheme {
|
||||
DiskScheme {
|
||||
next_id: AtomicUsize::new(0),
|
||||
data: Arc::new(RwLock::new(FILESYSTEM.to_vec())),
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for DiskScheme {
|
||||
fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
path: b"0",
|
||||
data: self.data.clone(),
|
||||
mode: MODE_FILE | 0o744,
|
||||
seek: 0
|
||||
});
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let (path, data, mode, seek) = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
(handle.path, handle.data.clone(), handle.mode, handle.seek)
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
path: path,
|
||||
data: data,
|
||||
mode: mode,
|
||||
seek: seek
|
||||
});
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let data = handle.data.read();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buffer.len() && handle.seek < data.len() {
|
||||
buffer[i] = data[handle.seek];
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buffer: &[u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let mut data = handle.data.write();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buffer.len() && handle.seek < data.len() {
|
||||
data[handle.seek] = buffer[i];
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let data = handle.data.read();
|
||||
|
||||
handle.seek = match whence {
|
||||
SEEK_SET => cmp::min(data.len(), pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(data.len() as isize, handle.seek as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(data.len() as isize, data.len() as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(handle.seek)
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
//TODO: Copy scheme part in kernel
|
||||
let mut i = 0;
|
||||
let scheme_path = b"disk:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < handle.path.len() {
|
||||
buf[i] = handle.path[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
let data = handle.data.read();
|
||||
|
||||
stat.st_mode = handle.mode;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = data.len() as u64;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let _handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
30
kernel/src/scheme/memory.rs
Normal file
30
kernel/src/scheme/memory.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use arch::memory::{free_frames, used_frames};
|
||||
|
||||
use syscall::data::StatVfs;
|
||||
use syscall::error::*;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub struct MemoryScheme;
|
||||
|
||||
impl Scheme for MemoryScheme {
|
||||
fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fstatvfs(&self, _file: usize, stat: &mut StatVfs) -> Result<usize> {
|
||||
let used = used_frames() as u64;
|
||||
let free = free_frames() as u64;
|
||||
|
||||
stat.f_bsize = 4096;
|
||||
stat.f_blocks = used + free;
|
||||
stat.f_bfree = free;
|
||||
stat.f_bavail = stat.f_bfree;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Close the file `number`
|
||||
fn close(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
246
kernel/src/scheme/mod.rs
Normal file
246
kernel/src/scheme/mod.rs
Normal file
|
@ -0,0 +1,246 @@
|
|||
//! # Schemes
|
||||
//! A scheme is a primitive for handling filesystem syscalls in Redox.
|
||||
//! Schemes accept paths from the kernel for `open`, and file descriptors that they generate
|
||||
//! are then passed for operations like `close`, `read`, `write`, etc.
|
||||
//!
|
||||
//! The kernel validates paths and file descriptors before they are passed to schemes,
|
||||
//! also stripping the scheme identifier of paths if necessary.
|
||||
|
||||
use alloc::arc::Arc;
|
||||
use alloc::boxed::Box;
|
||||
use collections::BTreeMap;
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use syscall::error::*;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
use self::debug::DebugScheme;
|
||||
use self::event::EventScheme;
|
||||
use self::env::EnvScheme;
|
||||
use self::initfs::InitFsScheme;
|
||||
use self::irq::IrqScheme;
|
||||
use self::memory::MemoryScheme;
|
||||
use self::null::NullScheme;
|
||||
use self::pipe::PipeScheme;
|
||||
use self::root::RootScheme;
|
||||
use self::sys::SysScheme;
|
||||
use self::zero::ZeroScheme;
|
||||
|
||||
/// `debug:` - provides access to serial console
|
||||
pub mod debug;
|
||||
|
||||
/// `event:` - allows reading of `Event`s which are registered using `fevent`
|
||||
pub mod event;
|
||||
|
||||
/// `env:` - access and modify environmental variables
|
||||
pub mod env;
|
||||
|
||||
/// `initfs:` - a readonly filesystem used for initializing the system
|
||||
pub mod initfs;
|
||||
|
||||
/// `irq:` - allows userspace handling of IRQs
|
||||
pub mod irq;
|
||||
|
||||
/// When compiled with "live" feature - `disk:` - embedded filesystem for live disk
|
||||
#[cfg(feature="live")]
|
||||
pub mod live;
|
||||
|
||||
/// `memory:` - a scheme for accessing physical memory
|
||||
pub mod memory;
|
||||
|
||||
/// `null:` - a scheme that will discard all writes, and read no bytes
|
||||
pub mod null;
|
||||
|
||||
/// `pipe:` - used internally by the kernel to implement `pipe`
|
||||
pub mod pipe;
|
||||
|
||||
/// `:` - allows the creation of userspace schemes, tightly dependent on `user`
|
||||
pub mod root;
|
||||
|
||||
/// `sys:` - system information, such as the context list and scheme list
|
||||
pub mod sys;
|
||||
|
||||
/// A wrapper around userspace schemes, tightly dependent on `root`
|
||||
pub mod user;
|
||||
|
||||
/// `zero:` - a scheme that will discard all writes, and always fill read buffers with zero
|
||||
pub mod zero;
|
||||
|
||||
/// Limit on number of schemes
|
||||
pub const SCHEME_MAX_SCHEMES: usize = 65536;
|
||||
|
||||
/// Unique identifier for a scheme namespace.
|
||||
int_like!(SchemeNamespace, AtomicSchemeNamespace, usize, AtomicUsize);
|
||||
|
||||
/// Unique identifier for a scheme.
|
||||
int_like!(SchemeId, AtomicSchemeId, usize, AtomicUsize);
|
||||
|
||||
pub const ATOMIC_SCHEMEID_INIT: AtomicSchemeId = AtomicSchemeId::default();
|
||||
|
||||
/// Unique identifier for a file descriptor.
|
||||
int_like!(FileHandle, AtomicFileHandle, usize, AtomicUsize);
|
||||
|
||||
/// Scheme list type
|
||||
pub struct SchemeList {
|
||||
map: BTreeMap<SchemeId, Arc<Box<Scheme + Send + Sync>>>,
|
||||
names: BTreeMap<SchemeNamespace, BTreeMap<Box<[u8]>, SchemeId>>,
|
||||
next_ns: usize,
|
||||
next_id: usize
|
||||
}
|
||||
|
||||
impl SchemeList {
|
||||
/// Create a new scheme list.
|
||||
pub fn new() -> Self {
|
||||
let mut list = SchemeList {
|
||||
map: BTreeMap::new(),
|
||||
names: BTreeMap::new(),
|
||||
next_ns: 0,
|
||||
next_id: 1
|
||||
};
|
||||
list.new_root();
|
||||
list
|
||||
}
|
||||
|
||||
/// Initialize a new namespace
|
||||
fn new_ns(&mut self) -> SchemeNamespace {
|
||||
let ns = SchemeNamespace(self.next_ns);
|
||||
self.next_ns += 1;
|
||||
self.names.insert(ns, BTreeMap::new());
|
||||
|
||||
self.insert(ns, Box::new(*b""), |scheme_id| Arc::new(Box::new(RootScheme::new(ns, scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"event"), |_| Arc::new(Box::new(EventScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"env"), |_| Arc::new(Box::new(EnvScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"null"), |_| Arc::new(Box::new(NullScheme))).unwrap();
|
||||
self.insert(ns, Box::new(*b"sys"), |_| Arc::new(Box::new(SysScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"zero"), |_| Arc::new(Box::new(ZeroScheme))).unwrap();
|
||||
|
||||
ns
|
||||
}
|
||||
|
||||
/// Initialize the root namespace
|
||||
#[cfg(not(feature="live"))]
|
||||
fn new_root(&mut self) {
|
||||
// Do common namespace initialization
|
||||
let ns = self.new_ns();
|
||||
|
||||
// Debug, Initfs and IRQ are only available in the root namespace. Pipe is special
|
||||
self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(Box::new(DebugScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(Box::new(InitFsScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(Box::new(IrqScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"memory"), |_| Arc::new(Box::new(MemoryScheme))).unwrap();
|
||||
self.insert(ns, Box::new(*b"pipe"), |scheme_id| Arc::new(Box::new(PipeScheme::new(scheme_id)))).unwrap();
|
||||
}
|
||||
|
||||
/// Initialize the root namespace - with live disk
|
||||
#[cfg(feature="live")]
|
||||
fn new_root(&mut self) {
|
||||
// Do common namespace initialization
|
||||
let ns = self.new_ns();
|
||||
|
||||
// Debug, Disk, Initfs and IRQ are only available in the root namespace. Pipe is special
|
||||
self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(Box::new(DebugScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"disk"), |_| Arc::new(Box::new(self::live::DiskScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(Box::new(InitFsScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(Box::new(IrqScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"memory"), |_| Arc::new(Box::new(MemoryScheme))).unwrap();
|
||||
self.insert(ns, Box::new(*b"pipe"), |scheme_id| Arc::new(Box::new(PipeScheme::new(scheme_id)))).unwrap();
|
||||
}
|
||||
|
||||
pub fn make_ns(&mut self, from: SchemeNamespace, names: &[&[u8]]) -> Result<SchemeNamespace> {
|
||||
// Create an empty namespace
|
||||
let to = self.new_ns();
|
||||
|
||||
// Copy requested scheme IDs
|
||||
for name in names.iter() {
|
||||
let id = if let Some((id, _scheme)) = self.get_name(from, name) {
|
||||
id
|
||||
} else {
|
||||
return Err(Error::new(ENODEV));
|
||||
};
|
||||
|
||||
if let Some(ref mut names) = self.names.get_mut(&to) {
|
||||
assert!(names.insert(name.to_vec().into_boxed_slice(), id).is_none());
|
||||
} else {
|
||||
panic!("scheme namespace not found");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(to)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> ::collections::btree_map::Iter<SchemeId, Arc<Box<Scheme + Send + Sync>>> {
|
||||
self.map.iter()
|
||||
}
|
||||
|
||||
pub fn iter_name(&self, ns: SchemeNamespace) -> ::collections::btree_map::Iter<Box<[u8]>, SchemeId> {
|
||||
self.names[&ns].iter()
|
||||
}
|
||||
|
||||
/// Get the nth scheme.
|
||||
pub fn get(&self, id: SchemeId) -> Option<&Arc<Box<Scheme + Send + Sync>>> {
|
||||
self.map.get(&id)
|
||||
}
|
||||
|
||||
pub fn get_name(&self, ns: SchemeNamespace, name: &[u8]) -> Option<(SchemeId, &Arc<Box<Scheme + Send + Sync>>)> {
|
||||
if let Some(&id) = self.names[&ns].get(name) {
|
||||
self.get(id).map(|scheme| (id, scheme))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new scheme.
|
||||
pub fn insert<F>(&mut self, ns: SchemeNamespace, name: Box<[u8]>, scheme_fn: F) -> Result<SchemeId>
|
||||
where F: Fn(SchemeId) -> Arc<Box<Scheme + Send + Sync>>
|
||||
{
|
||||
if self.names[&ns].contains_key(&name) {
|
||||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
|
||||
if self.next_id >= SCHEME_MAX_SCHEMES {
|
||||
self.next_id = 1;
|
||||
}
|
||||
|
||||
while self.map.contains_key(&SchemeId(self.next_id)) {
|
||||
self.next_id += 1;
|
||||
}
|
||||
|
||||
/* Allow scheme list to grow if required
|
||||
if self.next_id >= SCHEME_MAX_SCHEMES {
|
||||
return Err(Error::new(EAGAIN));
|
||||
}
|
||||
*/
|
||||
|
||||
let id = SchemeId(self.next_id);
|
||||
self.next_id += 1;
|
||||
|
||||
let scheme = scheme_fn(id);
|
||||
|
||||
assert!(self.map.insert(id, scheme).is_none());
|
||||
if let Some(ref mut names) = self.names.get_mut(&ns) {
|
||||
assert!(names.insert(name, id).is_none());
|
||||
} else {
|
||||
panic!("scheme namespace not found");
|
||||
}
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Schemes list
|
||||
static SCHEMES: Once<RwLock<SchemeList>> = Once::new();
|
||||
|
||||
/// Initialize schemes, called if needed
|
||||
fn init_schemes() -> RwLock<SchemeList> {
|
||||
RwLock::new(SchemeList::new())
|
||||
}
|
||||
|
||||
/// Get the global schemes list, const
|
||||
pub fn schemes() -> RwLockReadGuard<'static, SchemeList> {
|
||||
SCHEMES.call_once(init_schemes).read()
|
||||
}
|
||||
|
||||
/// Get the global schemes list, mutable
|
||||
pub fn schemes_mut() -> RwLockWriteGuard<'static, SchemeList> {
|
||||
SCHEMES.call_once(init_schemes).write()
|
||||
}
|
37
kernel/src/scheme/null.rs
Normal file
37
kernel/src/scheme/null.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use syscall::error::*;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub struct NullScheme;
|
||||
|
||||
impl Scheme for NullScheme {
|
||||
fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn dup(&self, _file: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Read the file `number` into the `buffer`
|
||||
///
|
||||
/// Returns the number of bytes read
|
||||
fn read(&self, _file: usize, _buf: &mut [u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Write the `buffer` to the `file`
|
||||
///
|
||||
/// Returns the number of bytes written
|
||||
fn write(&self, _file: usize, buffer: &[u8]) -> Result<usize> {
|
||||
Ok(buffer.len())
|
||||
}
|
||||
|
||||
fn fsync(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Close the file `number`
|
||||
fn close(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
272
kernel/src/scheme/pipe.rs
Normal file
272
kernel/src/scheme/pipe.rs
Normal file
|
@ -0,0 +1,272 @@
|
|||
use alloc::arc::{Arc, Weak};
|
||||
use collections::{BTreeMap, VecDeque};
|
||||
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use scheme::{AtomicSchemeId, ATOMIC_SCHEMEID_INIT, SchemeId};
|
||||
|
||||
use sync::WaitCondition;
|
||||
use syscall::error::{Error, Result, EAGAIN, EBADF, EINVAL, EPIPE};
|
||||
use syscall::flag::{F_GETFL, F_SETFL, O_ACCMODE, O_CLOEXEC, O_NONBLOCK};
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
/// Pipes list
|
||||
pub static PIPE_SCHEME_ID: AtomicSchemeId = ATOMIC_SCHEMEID_INIT;
|
||||
static PIPE_NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
static PIPES: Once<RwLock<(BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)>> = Once::new();
|
||||
|
||||
/// Initialize pipes, called if needed
|
||||
fn init_pipes() -> RwLock<(BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
|
||||
RwLock::new((BTreeMap::new(), BTreeMap::new()))
|
||||
}
|
||||
|
||||
/// Get the global pipes list, const
|
||||
fn pipes() -> RwLockReadGuard<'static, (BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
|
||||
PIPES.call_once(init_pipes).read()
|
||||
}
|
||||
|
||||
/// Get the global schemes list, mutable
|
||||
fn pipes_mut() -> RwLockWriteGuard<'static, (BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
|
||||
PIPES.call_once(init_pipes).write()
|
||||
}
|
||||
|
||||
pub fn pipe(flags: usize) -> (usize, usize) {
|
||||
let mut pipes = pipes_mut();
|
||||
let read_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
||||
let write_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
||||
let read = PipeRead::new(flags);
|
||||
let write = PipeWrite::new(flags, &read);
|
||||
pipes.0.insert(read_id, Arc::new(read));
|
||||
pipes.1.insert(write_id, Arc::new(write));
|
||||
(read_id, write_id)
|
||||
}
|
||||
|
||||
pub struct PipeScheme;
|
||||
|
||||
impl PipeScheme {
|
||||
pub fn new(scheme_id: SchemeId) -> PipeScheme {
|
||||
PIPE_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
|
||||
PipeScheme
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for PipeScheme {
|
||||
fn dup(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let mut pipes = pipes_mut();
|
||||
|
||||
let read_option = if let Some(pipe) = pipes.0.get(&id) {
|
||||
Some(pipe.dup(buf)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(pipe) = read_option {
|
||||
let pipe_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
||||
pipes.0.insert(pipe_id, Arc::new(pipe));
|
||||
return Ok(pipe_id);
|
||||
}
|
||||
|
||||
let write_option = if let Some(pipe) = pipes.1.get(&id) {
|
||||
Some(pipe.dup(buf)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(pipe) = write_option {
|
||||
let pipe_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
||||
pipes.1.insert(pipe_id, Arc::new(pipe));
|
||||
return Ok(pipe_id);
|
||||
}
|
||||
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
// Clone to prevent deadlocks
|
||||
let pipe = {
|
||||
let pipes = pipes();
|
||||
pipes.0.get(&id).map(|pipe| pipe.clone()).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
pipe.read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
// Clone to prevent deadlocks
|
||||
let pipe = {
|
||||
let pipes = pipes();
|
||||
pipes.1.get(&id).map(|pipe| pipe.clone()).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
pipe.write(buf)
|
||||
}
|
||||
|
||||
fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
|
||||
let pipes = pipes();
|
||||
|
||||
if let Some(pipe) = pipes.0.get(&id) {
|
||||
return pipe.fcntl(cmd, arg);
|
||||
}
|
||||
|
||||
if let Some(pipe) = pipes.1.get(&id) {
|
||||
return pipe.fcntl(cmd, arg);
|
||||
}
|
||||
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn fsync(&self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
let mut pipes = pipes_mut();
|
||||
|
||||
drop(pipes.0.remove(&id));
|
||||
drop(pipes.1.remove(&id));
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read side of a pipe
|
||||
pub struct PipeRead {
|
||||
flags: AtomicUsize,
|
||||
condition: Arc<WaitCondition>,
|
||||
vec: Arc<Mutex<VecDeque<u8>>>
|
||||
}
|
||||
|
||||
impl PipeRead {
|
||||
pub fn new(flags: usize) -> Self {
|
||||
PipeRead {
|
||||
flags: AtomicUsize::new(flags),
|
||||
condition: Arc::new(WaitCondition::new()),
|
||||
vec: Arc::new(Mutex::new(VecDeque::new())),
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&self, buf: &[u8]) -> Result<Self> {
|
||||
if buf == b"exec" && self.flags.load(Ordering::SeqCst) & O_CLOEXEC == O_CLOEXEC {
|
||||
Err(Error::new(EBADF))
|
||||
} else {
|
||||
let mut flags = self.flags.load(Ordering::SeqCst);
|
||||
if buf.is_empty() {
|
||||
flags &= ! O_CLOEXEC;
|
||||
}
|
||||
Ok(PipeRead {
|
||||
flags: AtomicUsize::new(flags),
|
||||
condition: self.condition.clone(),
|
||||
vec: self.vec.clone()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn fcntl(&self, cmd: usize, arg: usize) -> Result<usize> {
|
||||
match cmd {
|
||||
F_GETFL => Ok(self.flags.load(Ordering::SeqCst)),
|
||||
F_SETFL => {
|
||||
self.flags.store(arg & ! O_ACCMODE, Ordering::SeqCst);
|
||||
Ok(0)
|
||||
},
|
||||
_ => Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
loop {
|
||||
{
|
||||
let mut vec = self.vec.lock();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() {
|
||||
if let Some(b) = vec.pop_front() {
|
||||
buf[i] = b;
|
||||
i += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
return Ok(i);
|
||||
}
|
||||
}
|
||||
|
||||
if Arc::weak_count(&self.vec) == 0 {
|
||||
return Ok(0);
|
||||
} else if self.flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK {
|
||||
return Err(Error::new(EAGAIN));
|
||||
} else {
|
||||
self.condition.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read side of a pipe
|
||||
pub struct PipeWrite {
|
||||
flags: AtomicUsize,
|
||||
condition: Arc<WaitCondition>,
|
||||
vec: Option<Weak<Mutex<VecDeque<u8>>>>
|
||||
}
|
||||
|
||||
impl PipeWrite {
|
||||
pub fn new(flags: usize, read: &PipeRead) -> Self {
|
||||
PipeWrite {
|
||||
flags: AtomicUsize::new(flags),
|
||||
condition: read.condition.clone(),
|
||||
vec: Some(Arc::downgrade(&read.vec)),
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&self, buf: &[u8]) -> Result<Self> {
|
||||
if buf == b"exec" && self.flags.load(Ordering::SeqCst) & O_CLOEXEC == O_CLOEXEC {
|
||||
Err(Error::new(EBADF))
|
||||
} else {
|
||||
let mut flags = self.flags.load(Ordering::SeqCst);
|
||||
if buf.is_empty() {
|
||||
flags &= ! O_CLOEXEC;
|
||||
}
|
||||
Ok(PipeWrite {
|
||||
flags: AtomicUsize::new(flags),
|
||||
condition: self.condition.clone(),
|
||||
vec: self.vec.clone()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn fcntl(&self, cmd: usize, arg: usize) -> Result<usize> {
|
||||
match cmd {
|
||||
F_GETFL => Ok(self.flags.load(Ordering::SeqCst)),
|
||||
F_SETFL => {
|
||||
self.flags.store(arg & ! O_ACCMODE, Ordering::SeqCst);
|
||||
Ok(0)
|
||||
},
|
||||
_ => Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
if let Some(ref vec_weak) = self.vec {
|
||||
if let Some(vec_lock) = vec_weak.upgrade() {
|
||||
let mut vec = vec_lock.lock();
|
||||
|
||||
for &b in buf.iter() {
|
||||
vec.push_back(b);
|
||||
}
|
||||
|
||||
self.condition.notify();
|
||||
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
Err(Error::new(EPIPE))
|
||||
}
|
||||
} else {
|
||||
panic!("PipeWrite dropped before write");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PipeWrite {
|
||||
fn drop(&mut self) {
|
||||
drop(self.vec.take());
|
||||
self.condition.notify();
|
||||
}
|
||||
}
|
116
kernel/src/scheme/root.rs
Normal file
116
kernel/src/scheme/root.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use alloc::arc::Arc;
|
||||
use alloc::boxed::Box;
|
||||
use collections::BTreeMap;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
use context;
|
||||
use syscall::error::*;
|
||||
use syscall::scheme::Scheme;
|
||||
use scheme::{self, SchemeNamespace, SchemeId};
|
||||
use scheme::user::{UserInner, UserScheme};
|
||||
|
||||
pub struct RootScheme {
|
||||
scheme_ns: SchemeNamespace,
|
||||
scheme_id: SchemeId,
|
||||
next_id: AtomicUsize,
|
||||
handles: RwLock<BTreeMap<usize, Arc<UserInner>>>
|
||||
}
|
||||
|
||||
impl RootScheme {
|
||||
pub fn new(scheme_ns: SchemeNamespace, scheme_id: SchemeId) -> RootScheme {
|
||||
RootScheme {
|
||||
scheme_ns: scheme_ns,
|
||||
scheme_id: scheme_id,
|
||||
next_id: AtomicUsize::new(0),
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for RootScheme {
|
||||
fn open(&self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result<usize> {
|
||||
if uid == 0 {
|
||||
let context = {
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
Arc::downgrade(&context)
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
let inner = {
|
||||
let mut schemes = scheme::schemes_mut();
|
||||
let inner = Arc::new(UserInner::new(self.scheme_id, id, flags, context));
|
||||
schemes.insert(self.scheme_ns, path.to_vec().into_boxed_slice(), |scheme_id| {
|
||||
inner.scheme_id.store(scheme_id, Ordering::SeqCst);
|
||||
Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))
|
||||
})?;
|
||||
inner
|
||||
};
|
||||
|
||||
self.handles.write().insert(id, inner);
|
||||
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&self, file: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let inner = {
|
||||
let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
inner.clone()
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
handles.insert(id, inner);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let inner = {
|
||||
let handles = self.handles.read();
|
||||
let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
inner.clone()
|
||||
};
|
||||
|
||||
inner.read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
|
||||
let inner = {
|
||||
let handles = self.handles.read();
|
||||
let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
inner.clone()
|
||||
};
|
||||
|
||||
inner.write(buf)
|
||||
}
|
||||
|
||||
fn fevent(&self, file: usize, flags: usize) -> Result<usize> {
|
||||
let inner = {
|
||||
let handles = self.handles.read();
|
||||
let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
inner.clone()
|
||||
};
|
||||
|
||||
inner.fevent(flags)
|
||||
}
|
||||
|
||||
fn fsync(&self, file: usize) -> Result<usize> {
|
||||
let inner = {
|
||||
let handles = self.handles.read();
|
||||
let inner = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
inner.clone()
|
||||
};
|
||||
|
||||
inner.fsync()
|
||||
}
|
||||
|
||||
fn close(&self, file: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&file).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
106
kernel/src/scheme/sys/context.rs
Normal file
106
kernel/src/scheme/sys/context.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use collections::{String, Vec};
|
||||
use core::str;
|
||||
|
||||
use context;
|
||||
use syscall::error::Result;
|
||||
|
||||
pub fn resource() -> Result<Vec<u8>> {
|
||||
let mut string = format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n",
|
||||
"PID",
|
||||
"PPID",
|
||||
"RUID",
|
||||
"RGID",
|
||||
"RNS",
|
||||
"EUID",
|
||||
"EGID",
|
||||
"ENS",
|
||||
"STAT",
|
||||
"CPU",
|
||||
"MEM",
|
||||
"NAME");
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
for (_id, context_lock) in contexts.iter() {
|
||||
let context = context_lock.read();
|
||||
|
||||
let mut stat_string = String::new();
|
||||
if context.stack.is_some() {
|
||||
stat_string.push('U');
|
||||
} else {
|
||||
stat_string.push('K');
|
||||
}
|
||||
match context.status {
|
||||
context::Status::Runnable => {
|
||||
stat_string.push('R');
|
||||
},
|
||||
context::Status::Blocked => if context.wake.is_some() {
|
||||
stat_string.push('S');
|
||||
} else {
|
||||
stat_string.push('B');
|
||||
},
|
||||
context::Status::Exited(_status) => {
|
||||
stat_string.push('Z');
|
||||
}
|
||||
}
|
||||
if context.running {
|
||||
stat_string.push('+');
|
||||
}
|
||||
|
||||
let cpu_string = if let Some(cpu_id) = context.cpu_id {
|
||||
format!("{}", cpu_id)
|
||||
} else {
|
||||
format!("?")
|
||||
};
|
||||
|
||||
let mut memory = 0;
|
||||
if let Some(ref kfx) = context.kstack {
|
||||
memory += kfx.len();
|
||||
}
|
||||
if let Some(ref kstack) = context.kstack {
|
||||
memory += kstack.len();
|
||||
}
|
||||
for shared_mem in context.image.iter() {
|
||||
shared_mem.with(|mem| {
|
||||
memory += mem.size();
|
||||
});
|
||||
}
|
||||
if let Some(ref heap) = context.heap {
|
||||
heap.with(|heap| {
|
||||
memory += heap.size();
|
||||
});
|
||||
}
|
||||
if let Some(ref stack) = context.stack {
|
||||
memory += stack.size();
|
||||
}
|
||||
|
||||
let memory_string = if memory >= 1024 * 1024 * 1024 {
|
||||
format!("{} GB", memory / 1024 / 1024 / 1024)
|
||||
} else if memory >= 1024 * 1024 {
|
||||
format!("{} MB", memory / 1024 / 1024)
|
||||
} else if memory >= 1024 {
|
||||
format!("{} KB", memory / 1024)
|
||||
} else {
|
||||
format!("{} B", memory)
|
||||
};
|
||||
|
||||
let name_bytes = context.name.lock();
|
||||
let name = str::from_utf8(&name_bytes).unwrap_or("");
|
||||
|
||||
string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n",
|
||||
context.id.into(),
|
||||
context.ppid.into(),
|
||||
context.ruid,
|
||||
context.rgid,
|
||||
context.rns.into(),
|
||||
context.euid,
|
||||
context.egid,
|
||||
context.ens.into(),
|
||||
stat_string,
|
||||
cpu_string,
|
||||
memory_string,
|
||||
name));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(string.into_bytes())
|
||||
}
|
13
kernel/src/scheme/sys/cpu.rs
Normal file
13
kernel/src/scheme/sys/cpu.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use collections::Vec;
|
||||
|
||||
use arch::device::cpu::cpu_info;
|
||||
use syscall::error::{Error, EIO, Result};
|
||||
|
||||
pub fn resource() -> Result<Vec<u8>> {
|
||||
let mut string = format!("CPUs: {}\n", ::cpu_count());
|
||||
|
||||
match cpu_info(&mut string) {
|
||||
Ok(()) => Ok(string.into_bytes()),
|
||||
Err(_) => Err(Error::new(EIO))
|
||||
}
|
||||
}
|
16
kernel/src/scheme/sys/exe.rs
Normal file
16
kernel/src/scheme/sys/exe.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use collections::Vec;
|
||||
|
||||
use context;
|
||||
use syscall::error::{Error, ESRCH, Result};
|
||||
|
||||
pub fn resource() -> Result<Vec<u8>> {
|
||||
let mut name = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let name = context.name.lock();
|
||||
name.clone()
|
||||
};
|
||||
name.push(b'\n');
|
||||
Ok(name)
|
||||
}
|
183
kernel/src/scheme/sys/mod.rs
Normal file
183
kernel/src/scheme/sys/mod.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use alloc::boxed::Box;
|
||||
use collections::{BTreeMap, Vec};
|
||||
use core::{cmp, str};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::{Error, EBADF, EINVAL, ENOENT, Result};
|
||||
use syscall::flag::{MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
mod context;
|
||||
mod cpu;
|
||||
mod exe;
|
||||
mod scheme;
|
||||
//mod interrupt;
|
||||
//mod log;
|
||||
//mod test;
|
||||
|
||||
struct Handle {
|
||||
path: &'static [u8],
|
||||
data: Vec<u8>,
|
||||
mode: u16,
|
||||
seek: usize
|
||||
}
|
||||
|
||||
type SysFn = Fn() -> Result<Vec<u8>> + Send + Sync;
|
||||
|
||||
/// System information scheme
|
||||
pub struct SysScheme {
|
||||
next_id: AtomicUsize,
|
||||
files: BTreeMap<&'static [u8], Box<SysFn>>,
|
||||
handles: RwLock<BTreeMap<usize, Handle>>
|
||||
}
|
||||
|
||||
impl SysScheme {
|
||||
pub fn new() -> SysScheme {
|
||||
let mut files: BTreeMap<&'static [u8], Box<SysFn>> = BTreeMap::new();
|
||||
|
||||
files.insert(b"context", Box::new(move || context::resource()));
|
||||
files.insert(b"cpu", Box::new(move || cpu::resource()));
|
||||
files.insert(b"exe", Box::new(move || exe::resource()));
|
||||
files.insert(b"scheme", Box::new(move || scheme::resource()));
|
||||
//files.insert(b"interrupt", Box::new(move || interrupt::resource()));
|
||||
//files.insert(b"log", Box::new(move || log::resource()));
|
||||
//files.insert(b"test", Box::new(move || test::resource()));
|
||||
|
||||
SysScheme {
|
||||
next_id: AtomicUsize::new(0),
|
||||
files: files,
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for SysScheme {
|
||||
fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let path_utf8 = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?;
|
||||
let path_trimmed = path_utf8.trim_matches('/');
|
||||
|
||||
if path_trimmed.is_empty() {
|
||||
let mut data = Vec::new();
|
||||
for entry in self.files.iter() {
|
||||
if ! data.is_empty() {
|
||||
data.push(b'\n');
|
||||
}
|
||||
data.extend_from_slice(entry.0);
|
||||
}
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
path: b"",
|
||||
data: data,
|
||||
mode: MODE_DIR | 0o444,
|
||||
seek: 0
|
||||
});
|
||||
return Ok(id)
|
||||
} else {
|
||||
//Have to iterate to get the path without allocation
|
||||
for entry in self.files.iter() {
|
||||
if entry.0 == &path_trimmed.as_bytes() {
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
path: entry.0,
|
||||
data: entry.1()?,
|
||||
mode: MODE_FILE | 0o444,
|
||||
seek: 0
|
||||
});
|
||||
return Ok(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
|
||||
fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let (path, data, mode, seek) = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
(handle.path, handle.data.clone(), handle.mode, handle.seek)
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
path: path,
|
||||
data: data,
|
||||
mode: mode,
|
||||
seek: seek
|
||||
});
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
while i < buffer.len() && handle.seek < handle.data.len() {
|
||||
buffer[i] = handle.data[handle.seek];
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
handle.seek = match whence {
|
||||
SEEK_SET => cmp::min(handle.data.len(), pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(handle.data.len() as isize, handle.seek as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(handle.data.len() as isize, handle.data.len() as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(handle.seek)
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
let scheme_path = b"sys:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < handle.path.len() {
|
||||
buf[i] = handle.path[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
stat.st_mode = handle.mode;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = handle.data.len() as u64;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
24
kernel/src/scheme/sys/scheme.rs
Normal file
24
kernel/src/scheme/sys/scheme.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use collections::Vec;
|
||||
|
||||
use context;
|
||||
use scheme;
|
||||
use syscall::error::{Error, ESRCH, Result};
|
||||
|
||||
pub fn resource() -> Result<Vec<u8>> {
|
||||
let scheme_ns = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.ens
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
let schemes = scheme::schemes();
|
||||
for (name, _scheme_lock) in schemes.iter_name(scheme_ns) {
|
||||
data.extend_from_slice(name);
|
||||
data.push(b'\n');
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
354
kernel/src/scheme/user.rs
Normal file
354
kernel/src/scheme/user.rs
Normal file
|
@ -0,0 +1,354 @@
|
|||
use alloc::arc::{Arc, Weak};
|
||||
use collections::BTreeMap;
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
use core::{mem, slice, usize};
|
||||
use spin::{Mutex, RwLock};
|
||||
|
||||
use arch;
|
||||
use arch::paging::{InactivePageTable, Page, VirtualAddress, entry};
|
||||
use arch::paging::temporary_page::TemporaryPage;
|
||||
use context::{self, Context};
|
||||
use context::memory::Grant;
|
||||
use scheme::{AtomicSchemeId, ATOMIC_SCHEMEID_INIT, SchemeId};
|
||||
use sync::{WaitQueue, WaitMap};
|
||||
use syscall::data::{Packet, Stat, StatVfs};
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{EVENT_READ, O_NONBLOCK};
|
||||
use syscall::number::*;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub struct UserInner {
|
||||
root_id: SchemeId,
|
||||
handle_id: usize,
|
||||
flags: usize,
|
||||
pub scheme_id: AtomicSchemeId,
|
||||
next_id: AtomicU64,
|
||||
context: Weak<RwLock<Context>>,
|
||||
todo: WaitQueue<Packet>,
|
||||
fmap: Mutex<BTreeMap<u64, (Weak<RwLock<Context>>, usize)>>,
|
||||
done: WaitMap<u64, usize>
|
||||
}
|
||||
|
||||
impl UserInner {
|
||||
pub fn new(root_id: SchemeId, handle_id: usize, flags: usize, context: Weak<RwLock<Context>>) -> UserInner {
|
||||
UserInner {
|
||||
root_id: root_id,
|
||||
handle_id: handle_id,
|
||||
flags: flags,
|
||||
scheme_id: ATOMIC_SCHEMEID_INIT,
|
||||
next_id: AtomicU64::new(1),
|
||||
context: context,
|
||||
todo: WaitQueue::new(),
|
||||
fmap: Mutex::new(BTreeMap::new()),
|
||||
done: WaitMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(&self, a: usize, b: usize, c: usize, d: usize) -> Result<usize> {
|
||||
let (pid, uid, gid) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.id, context.euid, context.egid)
|
||||
};
|
||||
|
||||
self.call_inner(Packet {
|
||||
id: self.next_id.fetch_add(1, Ordering::SeqCst),
|
||||
pid: pid.into(),
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
a: a,
|
||||
b: b,
|
||||
c: c,
|
||||
d: d
|
||||
})
|
||||
}
|
||||
|
||||
fn call_inner(&self, packet: Packet) -> Result<usize> {
|
||||
let id = packet.id;
|
||||
|
||||
let len = self.todo.send(packet);
|
||||
context::event::trigger(self.root_id, self.handle_id, EVENT_READ, mem::size_of::<Packet>() * len);
|
||||
|
||||
Error::demux(self.done.receive(&id))
|
||||
}
|
||||
|
||||
pub fn capture(&self, buf: &[u8]) -> Result<usize> {
|
||||
UserInner::capture_inner(&self.context, buf.as_ptr() as usize, buf.len(), false)
|
||||
}
|
||||
|
||||
pub fn capture_mut(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
UserInner::capture_inner(&self.context, buf.as_mut_ptr() as usize, buf.len(), true)
|
||||
}
|
||||
|
||||
fn capture_inner(context_weak: &Weak<RwLock<Context>>, address: usize, size: usize, writable: bool) -> Result<usize> {
|
||||
if size == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let context_lock = context_weak.upgrade().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
let mut grants = context.grants.lock();
|
||||
|
||||
let mut new_table = unsafe { InactivePageTable::from_address(context.arch.get_page_table()) };
|
||||
let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(arch::USER_TMP_GRANT_OFFSET)));
|
||||
|
||||
let from_address = (address/4096) * 4096;
|
||||
let offset = address - from_address;
|
||||
let full_size = ((offset + size + 4095)/4096) * 4096;
|
||||
let mut to_address = arch::USER_GRANT_OFFSET;
|
||||
|
||||
let mut flags = entry::PRESENT | entry::NO_EXECUTE | entry::USER_ACCESSIBLE;
|
||||
if writable {
|
||||
flags |= entry::WRITABLE;
|
||||
}
|
||||
|
||||
for i in 0 .. grants.len() {
|
||||
let start = grants[i].start_address().get();
|
||||
if to_address + full_size < start {
|
||||
grants.insert(i, Grant::map_inactive(
|
||||
VirtualAddress::new(from_address),
|
||||
VirtualAddress::new(to_address),
|
||||
full_size,
|
||||
flags,
|
||||
&mut new_table,
|
||||
&mut temporary_page
|
||||
));
|
||||
|
||||
return Ok(to_address + offset);
|
||||
} else {
|
||||
let pages = (grants[i].size() + 4095) / 4096;
|
||||
let end = start + pages * 4096;
|
||||
to_address = end;
|
||||
}
|
||||
}
|
||||
|
||||
grants.push(Grant::map_inactive(
|
||||
VirtualAddress::new(from_address),
|
||||
VirtualAddress::new(to_address),
|
||||
full_size,
|
||||
flags,
|
||||
&mut new_table,
|
||||
&mut temporary_page
|
||||
));
|
||||
|
||||
Ok(to_address + offset)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release(&self, address: usize) -> Result<()> {
|
||||
if address == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
let context_lock = self.context.upgrade().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
let mut grants = context.grants.lock();
|
||||
|
||||
let mut new_table = unsafe { InactivePageTable::from_address(context.arch.get_page_table()) };
|
||||
let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(arch::USER_TMP_GRANT_OFFSET)));
|
||||
|
||||
for i in 0 .. grants.len() {
|
||||
let start = grants[i].start_address().get();
|
||||
let end = start + grants[i].size();
|
||||
if address >= start && address < end {
|
||||
grants.remove(i).unmap_inactive(&mut new_table, &mut temporary_page);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let packet_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Packet, buf.len()/mem::size_of::<Packet>()) };
|
||||
Ok(self.todo.receive_into(packet_buf, self.flags & O_NONBLOCK != O_NONBLOCK) * mem::size_of::<Packet>())
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
let packet_size = mem::size_of::<Packet>();
|
||||
let len = buf.len()/packet_size;
|
||||
let mut i = 0;
|
||||
while i < len {
|
||||
let mut packet = unsafe { *(buf.as_ptr() as *const Packet).offset(i as isize) };
|
||||
if packet.id == 0 {
|
||||
match packet.a {
|
||||
SYS_FEVENT => context::event::trigger(self.scheme_id.load(Ordering::SeqCst), packet.b, packet.c, packet.d),
|
||||
_ => println!("Unknown scheme -> kernel message {}", packet.a)
|
||||
}
|
||||
} else {
|
||||
if let Some((context_weak, size)) = self.fmap.lock().remove(&packet.id) {
|
||||
if let Ok(address) = Error::demux(packet.a) {
|
||||
packet.a = Error::mux(UserInner::capture_inner(&context_weak, address, size, true));
|
||||
}
|
||||
}
|
||||
|
||||
self.done.send(packet.id, packet.a);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * packet_size)
|
||||
}
|
||||
|
||||
pub fn fevent(&self, _flags: usize) -> Result<usize> {
|
||||
Ok(self.handle_id)
|
||||
}
|
||||
|
||||
pub fn fsync(&self) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// UserInner has to be wrapped
|
||||
pub struct UserScheme {
|
||||
inner: Weak<UserInner>
|
||||
}
|
||||
|
||||
impl UserScheme {
|
||||
pub fn new(inner: Weak<UserInner>) -> UserScheme {
|
||||
UserScheme {
|
||||
inner: inner
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for UserScheme {
|
||||
fn open(&self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(path)?;
|
||||
let result = inner.call(SYS_OPEN, address, path.len(), flags);
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn chmod(&self, path: &[u8], mode: u16, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(path)?;
|
||||
let result = inner.call(SYS_CHMOD, address, path.len(), mode as usize);
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn rmdir(&self, path: &[u8], _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(path)?;
|
||||
let result = inner.call(SYS_RMDIR, address, path.len(), 0);
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn unlink(&self, path: &[u8], _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(path)?;
|
||||
let result = inner.call(SYS_UNLINK, address, path.len(), 0);
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn dup(&self, file: usize, buf: &[u8]) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(buf)?;
|
||||
let result = inner.call(SYS_DUP, file, address, buf.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_mut(buf)?;
|
||||
let result = inner.call(SYS_READ, file, address, buf.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(buf)?;
|
||||
let result = inner.call(SYS_WRITE, file, address, buf.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn seek(&self, file: usize, position: usize, whence: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_LSEEK, file, position, whence)
|
||||
}
|
||||
|
||||
fn fcntl(&self, file: usize, cmd: usize, arg: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_FCNTL, file, cmd, arg)
|
||||
}
|
||||
|
||||
fn fevent(&self, file: usize, flags: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_FEVENT, file, flags, 0)
|
||||
}
|
||||
|
||||
fn fmap(&self, file: usize, offset: usize, size: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
|
||||
let (pid, uid, gid, context_lock) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.id, context.euid, context.egid, Arc::downgrade(&context_lock))
|
||||
};
|
||||
|
||||
let id = inner.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
inner.fmap.lock().insert(id, (context_lock, size));
|
||||
|
||||
inner.call_inner(Packet {
|
||||
id: id,
|
||||
pid: pid.into(),
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
a: SYS_FMAP,
|
||||
b: file,
|
||||
c: offset,
|
||||
d: size
|
||||
})
|
||||
}
|
||||
|
||||
fn fpath(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_mut(buf)?;
|
||||
let result = inner.call(SYS_FPATH, file, address, buf.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn fstat(&self, file: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_mut(stat)?;
|
||||
let result = inner.call(SYS_FSTAT, file, address, mem::size_of::<Stat>());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn fstatvfs(&self, file: usize, stat: &mut StatVfs) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_mut(stat)?;
|
||||
let result = inner.call(SYS_FSTATVFS, file, address, mem::size_of::<StatVfs>());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn fsync(&self, file: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_FSYNC, file, 0, 0)
|
||||
}
|
||||
|
||||
fn ftruncate(&self, file: usize, len: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_FTRUNCATE, file, len, 0)
|
||||
}
|
||||
|
||||
fn close(&self, file: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_CLOSE, file, 0, 0)
|
||||
}
|
||||
}
|
42
kernel/src/scheme/zero.rs
Normal file
42
kernel/src/scheme/zero.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use syscall::error::*;
|
||||
use syscall::scheme::Scheme;
|
||||
|
||||
pub struct ZeroScheme;
|
||||
|
||||
impl Scheme for ZeroScheme {
|
||||
fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn dup(&self, _file: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Read the file `number` into the `buffer`
|
||||
///
|
||||
/// Returns the number of bytes read
|
||||
fn read(&self, _file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut i = 0;
|
||||
while i < buf.len() {
|
||||
buf[i] = 0;
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Write the `buffer` to the `file`
|
||||
///
|
||||
/// Returns the number of bytes written
|
||||
fn write(&self, _file: usize, buffer: &[u8]) -> Result<usize> {
|
||||
Ok(buffer.len())
|
||||
}
|
||||
|
||||
fn fsync(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Close the file `number`
|
||||
fn close(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
7
kernel/src/sync/mod.rs
Normal file
7
kernel/src/sync/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
pub use self::wait_condition::WaitCondition;
|
||||
pub use self::wait_queue::WaitQueue;
|
||||
pub use self::wait_map::WaitMap;
|
||||
|
||||
pub mod wait_condition;
|
||||
pub mod wait_queue;
|
||||
pub mod wait_map;
|
48
kernel/src/sync/wait_condition.rs
Normal file
48
kernel/src/sync/wait_condition.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use alloc::arc::Arc;
|
||||
use collections::Vec;
|
||||
use spin::{Mutex, RwLock};
|
||||
|
||||
use context::{self, Context};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WaitCondition {
|
||||
contexts: Mutex<Vec<Arc<RwLock<Context>>>>
|
||||
}
|
||||
|
||||
impl WaitCondition {
|
||||
pub fn new() -> WaitCondition {
|
||||
WaitCondition {
|
||||
contexts: Mutex::new(Vec::with_capacity(16))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(&self) -> usize {
|
||||
let mut contexts = self.contexts.lock();
|
||||
let len = contexts.len();
|
||||
while let Some(context_lock) = contexts.pop() {
|
||||
context_lock.write().unblock();
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
pub fn wait(&self) {
|
||||
{
|
||||
let context_lock = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().expect("WaitCondition::wait: no context");
|
||||
context_lock.clone()
|
||||
};
|
||||
|
||||
context_lock.write().block();
|
||||
|
||||
self.contexts.lock().push(context_lock);
|
||||
}
|
||||
unsafe { context::switch(); }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WaitCondition {
|
||||
fn drop(&mut self){
|
||||
self.notify();
|
||||
}
|
||||
}
|
62
kernel/src/sync/wait_map.rs
Normal file
62
kernel/src/sync/wait_map.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use collections::BTreeMap;
|
||||
use core::mem;
|
||||
use spin::Mutex;
|
||||
|
||||
use sync::WaitCondition;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WaitMap<K, V> {
|
||||
inner: Mutex<BTreeMap<K, V>>,
|
||||
condition: WaitCondition
|
||||
}
|
||||
|
||||
impl<K, V> WaitMap<K, V> where K: Clone + Ord {
|
||||
pub fn new() -> WaitMap<K, V> {
|
||||
WaitMap {
|
||||
inner: Mutex::new(BTreeMap::new()),
|
||||
condition: WaitCondition::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_nonblock(&self, key: &K) -> Option<V> {
|
||||
self.inner.lock().remove(key)
|
||||
}
|
||||
|
||||
pub fn receive(&self, key: &K) -> V {
|
||||
loop {
|
||||
if let Some(value) = self.receive_nonblock(key) {
|
||||
return value;
|
||||
}
|
||||
self.condition.wait();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_any_nonblock(&self) -> Option<(K, V)> {
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(key) = inner.keys().next().map(|key| key.clone()) {
|
||||
inner.remove(&key).map(|value| (key, value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_any(&self) -> (K, V) {
|
||||
loop {
|
||||
if let Some(entry) = self.receive_any_nonblock() {
|
||||
return entry;
|
||||
}
|
||||
self.condition.wait();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_all(&self) -> BTreeMap<K, V> {
|
||||
let mut ret = BTreeMap::new();
|
||||
mem::swap(&mut ret, &mut *self.inner.lock());
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn send(&self, key: K, value: V) {
|
||||
self.inner.lock().insert(key, value);
|
||||
self.condition.notify();
|
||||
}
|
||||
}
|
82
kernel/src/sync/wait_queue.rs
Normal file
82
kernel/src/sync/wait_queue.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use collections::vec_deque::VecDeque;
|
||||
use spin::Mutex;
|
||||
|
||||
use sync::WaitCondition;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WaitQueue<T> {
|
||||
pub inner: Mutex<VecDeque<T>>,
|
||||
pub condition: WaitCondition,
|
||||
}
|
||||
|
||||
impl<T> WaitQueue<T> {
|
||||
pub fn new() -> WaitQueue<T> {
|
||||
WaitQueue {
|
||||
inner: Mutex::new(VecDeque::new()),
|
||||
condition: WaitCondition::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone(&self) -> WaitQueue<T> where T: Clone {
|
||||
WaitQueue {
|
||||
inner: Mutex::new(self.inner.lock().clone()),
|
||||
condition: WaitCondition::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.lock().is_empty()
|
||||
}
|
||||
|
||||
pub fn receive(&self) -> T {
|
||||
loop {
|
||||
if let Some(value) = self.inner.lock().pop_front() {
|
||||
return value;
|
||||
}
|
||||
self.condition.wait();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_into(&self, buf: &mut [T], block: bool) -> usize {
|
||||
let mut i = 0;
|
||||
|
||||
if i < buf.len() && block {
|
||||
buf[i] = self.receive();
|
||||
i += 1;
|
||||
}
|
||||
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
while i < buf.len() {
|
||||
if let Some(value) = inner.pop_front() {
|
||||
buf[i] = value;
|
||||
i += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i
|
||||
}
|
||||
|
||||
pub fn send(&self, value: T) -> usize {
|
||||
let len = {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.push_back(value);
|
||||
inner.len()
|
||||
};
|
||||
self.condition.notify();
|
||||
len
|
||||
}
|
||||
|
||||
pub fn send_from(&self, buf: &[T]) -> usize where T: Copy {
|
||||
let len = {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.extend(buf.iter());
|
||||
inner.len()
|
||||
};
|
||||
self.condition.notify();
|
||||
len
|
||||
}
|
||||
}
|
130
kernel/src/syscall/driver.rs
Normal file
130
kernel/src/syscall/driver.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
use arch;
|
||||
use arch::memory::{allocate_frames, deallocate_frames, Frame};
|
||||
use arch::paging::{entry, ActivePageTable, PhysicalAddress, VirtualAddress};
|
||||
use context;
|
||||
use context::memory::Grant;
|
||||
use syscall::error::{Error, EFAULT, ENOMEM, EPERM, ESRCH, Result};
|
||||
use syscall::flag::{MAP_WRITE, MAP_WRITE_COMBINE};
|
||||
|
||||
fn enforce_root() -> Result<()> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
if context.euid == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(EPERM))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iopl(_level: usize, _stack_base: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
|
||||
//TODO
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn physalloc(size: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
|
||||
allocate_frames((size + 4095)/4096).ok_or(Error::new(ENOMEM)).map(|frame| frame.start_address().get())
|
||||
}
|
||||
|
||||
pub fn physfree(physical_address: usize, size: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
|
||||
deallocate_frames(Frame::containing_address(PhysicalAddress::new(physical_address)), (size + 4095)/4096);
|
||||
//TODO: Check that no double free occured
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
//TODO: verify exlusive access to physical memory
|
||||
pub fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
|
||||
if size == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
let mut grants = context.grants.lock();
|
||||
|
||||
let from_address = (physical_address/4096) * 4096;
|
||||
let offset = physical_address - from_address;
|
||||
let full_size = ((offset + size + 4095)/4096) * 4096;
|
||||
let mut to_address = arch::USER_GRANT_OFFSET;
|
||||
|
||||
let mut entry_flags = entry::PRESENT | entry::NO_EXECUTE | entry::USER_ACCESSIBLE;
|
||||
if flags & MAP_WRITE == MAP_WRITE {
|
||||
entry_flags |= entry::WRITABLE;
|
||||
}
|
||||
if flags & MAP_WRITE_COMBINE == MAP_WRITE_COMBINE {
|
||||
entry_flags |= entry::HUGE_PAGE;
|
||||
}
|
||||
|
||||
for i in 0 .. grants.len() {
|
||||
let start = grants[i].start_address().get();
|
||||
if to_address + full_size < start {
|
||||
grants.insert(i, Grant::physmap(
|
||||
PhysicalAddress::new(from_address),
|
||||
VirtualAddress::new(to_address),
|
||||
full_size,
|
||||
entry_flags
|
||||
));
|
||||
|
||||
return Ok(to_address + offset);
|
||||
} else {
|
||||
let pages = (grants[i].size() + 4095) / 4096;
|
||||
let end = start + pages * 4096;
|
||||
to_address = end;
|
||||
}
|
||||
}
|
||||
|
||||
grants.push(Grant::physmap(
|
||||
PhysicalAddress::new(from_address),
|
||||
VirtualAddress::new(to_address),
|
||||
full_size,
|
||||
entry_flags
|
||||
));
|
||||
|
||||
Ok(to_address + offset)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn physunmap(virtual_address: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
|
||||
if virtual_address == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
let mut grants = context.grants.lock();
|
||||
|
||||
for i in 0 .. grants.len() {
|
||||
let start = grants[i].start_address().get();
|
||||
let end = start + grants[i].size();
|
||||
if virtual_address >= start && virtual_address < end {
|
||||
grants.remove(i).unmap();
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn virttophys(virtual_address: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
|
||||
let active_table = unsafe { ActivePageTable::new() };
|
||||
match active_table.translate(VirtualAddress::new(virtual_address)) {
|
||||
Some(physical_address) => Ok(physical_address.get()),
|
||||
None => Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
356
kernel/src/syscall/fs.rs
Normal file
356
kernel/src/syscall/fs.rs
Normal file
|
@ -0,0 +1,356 @@
|
|||
//! Filesystem syscalls
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use context;
|
||||
use scheme::{self, FileHandle};
|
||||
use syscall;
|
||||
use syscall::data::{Packet, Stat};
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{MODE_DIR, MODE_FILE};
|
||||
|
||||
pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result<usize> {
|
||||
let (file, pid, uid, gid) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
|
||||
(file, context.id, context.euid, context.egid)
|
||||
};
|
||||
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
|
||||
scheme.clone()
|
||||
};
|
||||
|
||||
let mut packet = Packet {
|
||||
id: 0,
|
||||
pid: pid.into(),
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
a: a,
|
||||
b: file.number,
|
||||
c: c,
|
||||
d: d
|
||||
};
|
||||
|
||||
scheme.handle(&mut packet);
|
||||
|
||||
Error::demux(packet.a)
|
||||
}
|
||||
|
||||
pub fn file_op_slice(a: usize, fd: FileHandle, slice: &[u8]) -> Result<usize> {
|
||||
file_op(a, fd, slice.as_ptr() as usize, slice.len())
|
||||
}
|
||||
|
||||
pub fn file_op_mut_slice(a: usize, fd: FileHandle, slice: &mut [u8]) -> Result<usize> {
|
||||
file_op(a, fd, slice.as_mut_ptr() as usize, slice.len())
|
||||
}
|
||||
|
||||
/// Change the current working directory
|
||||
pub fn chdir(path: &[u8]) -> Result<usize> {
|
||||
let fd = open(path, syscall::flag::O_RDONLY | syscall::flag::O_DIRECTORY)?;
|
||||
let mut stat = Stat::default();
|
||||
let stat_res = file_op_mut_slice(syscall::number::SYS_FSTAT, fd, &mut stat);
|
||||
let _ = close(fd);
|
||||
stat_res?;
|
||||
if stat.st_mode & (MODE_FILE | MODE_DIR) == MODE_DIR {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let canonical = context.canonicalize(path);
|
||||
*context.cwd.lock() = canonical;
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(ENOTDIR))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current working directory
|
||||
pub fn getcwd(buf: &mut [u8]) -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let cwd = context.cwd.lock();
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < cwd.len() {
|
||||
buf[i] = cwd[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Open syscall
|
||||
pub fn open(path: &[u8], flags: usize) -> Result<FileHandle> {
|
||||
let (path_canon, uid, gid, scheme_ns) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.canonicalize(path), context.euid, context.egid, context.ens)
|
||||
};
|
||||
|
||||
//println!("open {}", unsafe { ::core::str::from_utf8_unchecked(&path_canon) });
|
||||
|
||||
let mut parts = path_canon.splitn(2, |&b| b == b':');
|
||||
let scheme_name_opt = parts.next();
|
||||
let reference_opt = parts.next();
|
||||
|
||||
let (scheme_id, file_id) = {
|
||||
let scheme_name = scheme_name_opt.ok_or(Error::new(ENODEV))?;
|
||||
let (scheme_id, scheme) = {
|
||||
let schemes = scheme::schemes();
|
||||
let (scheme_id, scheme) = schemes.get_name(scheme_ns, scheme_name).ok_or(Error::new(ENODEV))?;
|
||||
(scheme_id, scheme.clone())
|
||||
};
|
||||
let file_id = scheme.open(reference_opt.unwrap_or(b""), flags, uid, gid)?;
|
||||
(scheme_id, file_id)
|
||||
};
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.add_file(::context::file::File {
|
||||
scheme: scheme_id,
|
||||
number: file_id,
|
||||
event: None,
|
||||
}).ok_or(Error::new(EMFILE))
|
||||
}
|
||||
|
||||
pub fn pipe2(fds: &mut [usize], flags: usize) -> Result<usize> {
|
||||
if fds.len() >= 2 {
|
||||
let scheme_id = ::scheme::pipe::PIPE_SCHEME_ID.load(Ordering::SeqCst);
|
||||
let (read_id, write_id) = ::scheme::pipe::pipe(flags);
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
let read_fd = context.add_file(::context::file::File {
|
||||
scheme: scheme_id,
|
||||
number: read_id,
|
||||
event: None,
|
||||
}).ok_or(Error::new(EMFILE))?;
|
||||
|
||||
let write_fd = context.add_file(::context::file::File {
|
||||
scheme: scheme_id,
|
||||
number: write_id,
|
||||
event: None,
|
||||
}).ok_or(Error::new(EMFILE))?;
|
||||
|
||||
fds[0] = read_fd.into();
|
||||
fds[1] = write_fd.into();
|
||||
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
||||
|
||||
/// chmod syscall
|
||||
pub fn chmod(path: &[u8], mode: u16) -> Result<usize> {
|
||||
let (path_canon, uid, gid, scheme_ns) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.canonicalize(path), context.euid, context.egid, context.ens)
|
||||
};
|
||||
|
||||
let mut parts = path_canon.splitn(2, |&b| b == b':');
|
||||
let scheme_name_opt = parts.next();
|
||||
let reference_opt = parts.next();
|
||||
|
||||
let scheme_name = scheme_name_opt.ok_or(Error::new(ENODEV))?;
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let (_scheme_id, scheme) = schemes.get_name(scheme_ns, scheme_name).ok_or(Error::new(ENODEV))?;
|
||||
scheme.clone()
|
||||
};
|
||||
scheme.chmod(reference_opt.unwrap_or(b""), mode, uid, gid)
|
||||
}
|
||||
|
||||
/// rmdir syscall
|
||||
pub fn rmdir(path: &[u8]) -> Result<usize> {
|
||||
let (path_canon, uid, gid, scheme_ns) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.canonicalize(path), context.euid, context.egid, context.ens)
|
||||
};
|
||||
|
||||
let mut parts = path_canon.splitn(2, |&b| b == b':');
|
||||
let scheme_name_opt = parts.next();
|
||||
let reference_opt = parts.next();
|
||||
|
||||
let scheme_name = scheme_name_opt.ok_or(Error::new(ENODEV))?;
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let (_scheme_id, scheme) = schemes.get_name(scheme_ns, scheme_name).ok_or(Error::new(ENODEV))?;
|
||||
scheme.clone()
|
||||
};
|
||||
scheme.rmdir(reference_opt.unwrap_or(b""), uid, gid)
|
||||
}
|
||||
|
||||
/// Unlink syscall
|
||||
pub fn unlink(path: &[u8]) -> Result<usize> {
|
||||
let (path_canon, uid, gid, scheme_ns) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.canonicalize(path), context.euid, context.egid, context.ens)
|
||||
};
|
||||
|
||||
let mut parts = path_canon.splitn(2, |&b| b == b':');
|
||||
let scheme_name_opt = parts.next();
|
||||
let reference_opt = parts.next();
|
||||
|
||||
let scheme_name = scheme_name_opt.ok_or(Error::new(ENODEV))?;
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let (_scheme_id, scheme) = schemes.get_name(scheme_ns, scheme_name).ok_or(Error::new(ENODEV))?;
|
||||
scheme.clone()
|
||||
};
|
||||
scheme.unlink(reference_opt.unwrap_or(b""), uid, gid)
|
||||
}
|
||||
|
||||
/// Close syscall
|
||||
pub fn close(fd: FileHandle) -> Result<usize> {
|
||||
let file = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let file = context.remove_file(fd).ok_or(Error::new(EBADF))?;
|
||||
file
|
||||
};
|
||||
|
||||
if let Some(event_id) = file.event {
|
||||
context::event::unregister(fd, file.scheme, event_id);
|
||||
}
|
||||
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
|
||||
scheme.clone()
|
||||
};
|
||||
scheme.close(file.number)
|
||||
}
|
||||
|
||||
/// Duplicate file descriptor
|
||||
pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
|
||||
let file = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
|
||||
file
|
||||
};
|
||||
|
||||
let new_id = {
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
|
||||
scheme.clone()
|
||||
};
|
||||
scheme.dup(file.number, buf)?
|
||||
};
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.add_file(::context::file::File {
|
||||
scheme: file.scheme,
|
||||
number: new_id,
|
||||
event: None,
|
||||
}).ok_or(Error::new(EMFILE))
|
||||
}
|
||||
|
||||
/// Duplicate file descriptor, replacing another
|
||||
pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
|
||||
if fd == new_fd {
|
||||
Ok(new_fd)
|
||||
} else {
|
||||
let _ = close(new_fd)?;
|
||||
|
||||
let file = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
|
||||
file
|
||||
};
|
||||
|
||||
let new_id = {
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
|
||||
scheme.clone()
|
||||
};
|
||||
scheme.dup(file.number, buf)?
|
||||
};
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.insert_file(new_fd, ::context::file::File {
|
||||
scheme: file.scheme,
|
||||
number: new_id,
|
||||
event: None,
|
||||
}).ok_or(Error::new(EBADF))
|
||||
}
|
||||
}
|
||||
|
||||
/// Register events for file
|
||||
pub fn fevent(fd: FileHandle, flags: usize) -> Result<usize> {
|
||||
let file = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let mut files = context.files.lock();
|
||||
let mut file = files.get_mut(fd.into()).ok_or(Error::new(EBADF))?.ok_or(Error::new(EBADF))?;
|
||||
if let Some(event_id) = file.event.take() {
|
||||
println!("{:?}: {:?}:{}: events already registered: {}", fd, file.scheme, file.number, event_id);
|
||||
context::event::unregister(fd, file.scheme, event_id);
|
||||
}
|
||||
file.clone()
|
||||
};
|
||||
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
|
||||
scheme.clone()
|
||||
};
|
||||
let event_id = scheme.fevent(file.number, flags)?;
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let mut files = context.files.lock();
|
||||
let mut file = files.get_mut(fd.into()).ok_or(Error::new(EBADF))?.ok_or(Error::new(EBADF))?;
|
||||
file.event = Some(event_id);
|
||||
}
|
||||
context::event::register(fd, file.scheme, event_id);
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn funmap(virtual_address: usize) -> Result<usize> {
|
||||
if virtual_address == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
let mut grants = context.grants.lock();
|
||||
|
||||
for i in 0 .. grants.len() {
|
||||
let start = grants[i].start_address().get();
|
||||
let end = start + grants[i].size();
|
||||
if virtual_address >= start && virtual_address < end {
|
||||
grants.remove(i).unmap();
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
110
kernel/src/syscall/futex.rs
Normal file
110
kernel/src/syscall/futex.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
use alloc::arc::Arc;
|
||||
use collections::VecDeque;
|
||||
use core::intrinsics;
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use context::{self, Context};
|
||||
use syscall::error::{Error, Result, ESRCH, EAGAIN, EINVAL};
|
||||
use syscall::flag::{FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE};
|
||||
use syscall::validate::validate_slice_mut;
|
||||
|
||||
type FutexList = VecDeque<(usize, Arc<RwLock<Context>>)>;
|
||||
|
||||
/// Fast userspace mutex list
|
||||
static FUTEXES: Once<RwLock<FutexList>> = Once::new();
|
||||
|
||||
/// Initialize futexes, called if needed
|
||||
fn init_futexes() -> RwLock<FutexList> {
|
||||
RwLock::new(VecDeque::new())
|
||||
}
|
||||
|
||||
/// Get the global futexes list, const
|
||||
pub fn futexes() -> RwLockReadGuard<'static, FutexList> {
|
||||
FUTEXES.call_once(init_futexes).read()
|
||||
}
|
||||
|
||||
/// Get the global futexes list, mutable
|
||||
pub fn futexes_mut() -> RwLockWriteGuard<'static, FutexList> {
|
||||
FUTEXES.call_once(init_futexes).write()
|
||||
}
|
||||
|
||||
pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) -> Result<usize> {
|
||||
match op {
|
||||
FUTEX_WAIT => {
|
||||
{
|
||||
let mut futexes = futexes_mut();
|
||||
|
||||
let context_lock = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
context_lock.clone()
|
||||
};
|
||||
|
||||
if unsafe { intrinsics::atomic_load(addr) != val } {
|
||||
return Err(Error::new(EAGAIN));
|
||||
}
|
||||
|
||||
context_lock.write().block();
|
||||
|
||||
futexes.push_back((addr as *mut i32 as usize, context_lock));
|
||||
}
|
||||
|
||||
unsafe { context::switch(); }
|
||||
|
||||
Ok(0)
|
||||
},
|
||||
FUTEX_WAKE => {
|
||||
let mut woken = 0;
|
||||
|
||||
{
|
||||
let mut futexes = futexes_mut();
|
||||
|
||||
let mut i = 0;
|
||||
while i < futexes.len() && (woken as i32) < val {
|
||||
if futexes[i].0 == addr as *mut i32 as usize {
|
||||
if let Some(futex) = futexes.swap_remove_back(i) {
|
||||
futex.1.write().unblock();
|
||||
woken += 1;
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(woken)
|
||||
},
|
||||
FUTEX_REQUEUE => {
|
||||
let addr2_safe = validate_slice_mut(addr2, 1).map(|addr2_safe| &mut addr2_safe[0])?;
|
||||
|
||||
let mut woken = 0;
|
||||
let mut requeued = 0;
|
||||
|
||||
{
|
||||
let mut futexes = futexes_mut();
|
||||
|
||||
let mut i = 0;
|
||||
while i < futexes.len() && (woken as i32) < val {
|
||||
if futexes[i].0 == addr as *mut i32 as usize {
|
||||
if let Some(futex) = futexes.swap_remove_back(i) {
|
||||
futex.1.write().unblock();
|
||||
woken += 1;
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
while i < futexes.len() && requeued < val2 {
|
||||
if futexes[i].0 == addr as *mut i32 as usize {
|
||||
futexes[i].0 = addr2_safe as *mut i32 as usize;
|
||||
requeued += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(woken)
|
||||
},
|
||||
_ => Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
121
kernel/src/syscall/mod.rs
Normal file
121
kernel/src/syscall/mod.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
///! Syscall handlers
|
||||
|
||||
extern crate syscall;
|
||||
|
||||
pub use self::syscall::{data, error, flag, number, scheme};
|
||||
|
||||
pub use self::driver::*;
|
||||
pub use self::fs::*;
|
||||
pub use self::futex::futex;
|
||||
pub use self::privilege::*;
|
||||
pub use self::process::*;
|
||||
pub use self::time::*;
|
||||
pub use self::validate::*;
|
||||
|
||||
use self::data::TimeSpec;
|
||||
use self::error::{Error, Result, ENOSYS};
|
||||
use self::number::*;
|
||||
|
||||
use context::ContextId;
|
||||
use scheme::{FileHandle, SchemeNamespace};
|
||||
|
||||
/// Driver syscalls
|
||||
pub mod driver;
|
||||
|
||||
/// Filesystem syscalls
|
||||
pub mod fs;
|
||||
|
||||
/// Fast userspace mutex
|
||||
pub mod futex;
|
||||
|
||||
/// Privilege syscalls
|
||||
pub mod privilege;
|
||||
|
||||
/// Process syscalls
|
||||
pub mod process;
|
||||
|
||||
/// Time syscalls
|
||||
pub mod time;
|
||||
|
||||
/// Validate input
|
||||
pub mod validate;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> usize {
|
||||
#[inline(always)]
|
||||
fn inner(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> Result<usize> {
|
||||
match a & SYS_CLASS {
|
||||
SYS_CLASS_FILE => {
|
||||
let fd = FileHandle::from(b);
|
||||
match a & SYS_ARG {
|
||||
SYS_ARG_SLICE => file_op_slice(a, fd, validate_slice(c as *const u8, d)?),
|
||||
SYS_ARG_MSLICE => file_op_mut_slice(a, fd, validate_slice_mut(c as *mut u8, d)?),
|
||||
_ => match a {
|
||||
SYS_CLOSE => close(fd),
|
||||
SYS_DUP => dup(fd, validate_slice(c as *const u8, d)?).map(FileHandle::into),
|
||||
SYS_DUP2 => dup2(fd, FileHandle::from(c), validate_slice(d as *const u8, e)?).map(FileHandle::into),
|
||||
SYS_FEVENT => fevent(fd, c),
|
||||
SYS_FUNMAP => funmap(b),
|
||||
_ => file_op(a, fd, c, d)
|
||||
}
|
||||
}
|
||||
},
|
||||
SYS_CLASS_PATH => match a {
|
||||
SYS_OPEN => open(validate_slice(b as *const u8, c)?, d).map(FileHandle::into),
|
||||
SYS_CHMOD => chmod(validate_slice(b as *const u8, c)?, d as u16),
|
||||
SYS_RMDIR => rmdir(validate_slice(b as *const u8, c)?),
|
||||
SYS_UNLINK => unlink(validate_slice(b as *const u8, c)?),
|
||||
_ => unreachable!()
|
||||
},
|
||||
_ => match a {
|
||||
SYS_YIELD => sched_yield(),
|
||||
SYS_NANOSLEEP => nanosleep(validate_slice(b as *const TimeSpec, 1).map(|req| &req[0])?, validate_slice_mut(c as *mut TimeSpec, 1).ok().map(|rem| &mut rem[0])),
|
||||
SYS_CLOCK_GETTIME => clock_gettime(b, validate_slice_mut(c as *mut TimeSpec, 1).map(|time| &mut time[0])?),
|
||||
SYS_FUTEX => futex(validate_slice_mut(b as *mut i32, 1).map(|uaddr| &mut uaddr[0])?, c, d as i32, e, f as *mut i32),
|
||||
SYS_BRK => brk(b),
|
||||
SYS_GETPID => getpid().map(ContextId::into),
|
||||
SYS_CLONE => clone(b, stack).map(ContextId::into),
|
||||
SYS_EXIT => exit((b & 0xFF) << 8),
|
||||
SYS_KILL => kill(ContextId::from(b), c),
|
||||
SYS_WAITPID => waitpid(ContextId::from(b), c, d).map(ContextId::into),
|
||||
SYS_CHDIR => chdir(validate_slice(b as *const u8, c)?),
|
||||
SYS_EXECVE => exec(validate_slice(b as *const u8, c)?, validate_slice(d as *const [usize; 2], e)?),
|
||||
SYS_IOPL => iopl(b, stack),
|
||||
SYS_GETCWD => getcwd(validate_slice_mut(b as *mut u8, c)?),
|
||||
SYS_GETEGID => getegid(),
|
||||
SYS_GETENS => getens(),
|
||||
SYS_GETEUID => geteuid(),
|
||||
SYS_GETGID => getgid(),
|
||||
SYS_GETNS => getns(),
|
||||
SYS_GETUID => getuid(),
|
||||
SYS_MKNS => mkns(validate_slice(b as *const [usize; 2], c)?),
|
||||
SYS_SETREUID => setreuid(b as u32, c as u32),
|
||||
SYS_SETRENS => setrens(SchemeNamespace::from(b), SchemeNamespace::from(c)),
|
||||
SYS_SETREGID => setregid(b as u32, c as u32),
|
||||
SYS_PIPE2 => pipe2(validate_slice_mut(b as *mut usize, 2)?, c),
|
||||
SYS_PHYSALLOC => physalloc(b),
|
||||
SYS_PHYSFREE => physfree(b, c),
|
||||
SYS_PHYSMAP => physmap(b, c, d),
|
||||
SYS_PHYSUNMAP => physunmap(b),
|
||||
SYS_VIRTTOPHYS => virttophys(b),
|
||||
_ => Err(Error::new(ENOSYS))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = inner(a, b, c, d, e, f, stack);
|
||||
|
||||
/*
|
||||
if let Err(ref err) = result {
|
||||
let contexts = ::context::contexts();
|
||||
if let Some(context_lock) = contexts.current() {
|
||||
let context = context_lock.read();
|
||||
print!("{}: {}: ", unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) }, context.id.into());
|
||||
}
|
||||
|
||||
println!("{:X}, {:X}, {:X}, {:X}: {}", a, b, c, d, err);
|
||||
}
|
||||
*/
|
||||
|
||||
Error::mux(result)
|
||||
}
|
147
kernel/src/syscall/privilege.rs
Normal file
147
kernel/src/syscall/privilege.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
use collections::Vec;
|
||||
|
||||
use context;
|
||||
use scheme::{self, SchemeNamespace};
|
||||
use syscall::error::*;
|
||||
use syscall::validate::validate_slice;
|
||||
|
||||
pub fn getegid() -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
Ok(context.egid as usize)
|
||||
}
|
||||
|
||||
pub fn getens() -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
Ok(context.ens.into())
|
||||
}
|
||||
|
||||
pub fn geteuid() -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
Ok(context.euid as usize)
|
||||
}
|
||||
|
||||
pub fn getgid() -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
Ok(context.rgid as usize)
|
||||
}
|
||||
|
||||
pub fn getns() -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
Ok(context.rns.into())
|
||||
}
|
||||
|
||||
pub fn getuid() -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
Ok(context.ruid as usize)
|
||||
}
|
||||
|
||||
pub fn mkns(name_ptrs: &[[usize; 2]]) -> Result<usize> {
|
||||
let mut names = Vec::new();
|
||||
for name_ptr in name_ptrs {
|
||||
names.push(validate_slice(name_ptr[0] as *const u8, name_ptr[1])?);
|
||||
}
|
||||
|
||||
let (uid, from) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.euid, context.ens)
|
||||
};
|
||||
|
||||
if uid == 0 {
|
||||
let to = scheme::schemes_mut().make_ns(from, &names)?;
|
||||
Ok(to.into())
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setregid(rgid: u32, egid: u32) -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context_lock.write();
|
||||
|
||||
if (context.euid == 0
|
||||
|| rgid as i32 == -1
|
||||
|| rgid == context.egid
|
||||
|| rgid == context.rgid)
|
||||
&& (context.euid == 0
|
||||
|| egid as i32 == -1
|
||||
|| egid == context.egid
|
||||
|| egid == context.rgid)
|
||||
{
|
||||
if rgid as i32 != -1 {
|
||||
context.rgid = rgid;
|
||||
}
|
||||
if egid as i32 != -1 {
|
||||
context.egid = egid;
|
||||
}
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EPERM))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setrens(rns: SchemeNamespace, ens: SchemeNamespace) -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context_lock.write();
|
||||
|
||||
if (context.euid == 0
|
||||
|| rns.into() as isize == -1
|
||||
|| rns == context.ens
|
||||
|| rns == context.rns)
|
||||
&& (context.euid == 0
|
||||
|| ens.into() as isize == -1
|
||||
|| ens == context.ens
|
||||
|| ens == context.rns)
|
||||
{
|
||||
if rns.into() as isize != -1 {
|
||||
context.rns = rns;
|
||||
}
|
||||
if ens.into() as isize != -1 {
|
||||
context.ens = ens;
|
||||
}
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EPERM))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setreuid(ruid: u32, euid: u32) -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context_lock.write();
|
||||
|
||||
if (context.euid == 0
|
||||
|| ruid as i32 == -1
|
||||
|| ruid == context.euid
|
||||
|| ruid == context.ruid)
|
||||
&& (context.euid == 0
|
||||
|| euid as i32 == -1
|
||||
|| euid == context.euid
|
||||
|| euid == context.ruid)
|
||||
{
|
||||
if ruid as i32 != -1 {
|
||||
context.ruid = ruid;
|
||||
}
|
||||
if euid as i32 != -1 {
|
||||
context.euid = euid;
|
||||
}
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EPERM))
|
||||
}
|
||||
}
|
965
kernel/src/syscall/process.rs
Normal file
965
kernel/src/syscall/process.rs
Normal file
|
@ -0,0 +1,965 @@
|
|||
///! Process syscalls
|
||||
use alloc::arc::Arc;
|
||||
use alloc::boxed::Box;
|
||||
use collections::{BTreeMap, Vec};
|
||||
use core::{intrinsics, mem, str};
|
||||
use core::ops::DerefMut;
|
||||
use spin::Mutex;
|
||||
|
||||
use arch;
|
||||
use arch::memory::allocate_frame;
|
||||
use arch::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, entry};
|
||||
use arch::paging::temporary_page::TemporaryPage;
|
||||
use arch::start::usermode;
|
||||
use context;
|
||||
use context::ContextId;
|
||||
use elf::{self, program_header};
|
||||
use scheme::{self, FileHandle};
|
||||
use syscall;
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, WNOHANG};
|
||||
use syscall::validate::{validate_slice, validate_slice_mut};
|
||||
|
||||
pub fn brk(address: usize) -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
//println!("{}: {}: BRK {:X}", unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) },
|
||||
// context.id.into(), address);
|
||||
|
||||
let current = if let Some(ref heap_shared) = context.heap {
|
||||
heap_shared.with(|heap| {
|
||||
heap.start_address().get() + heap.size()
|
||||
})
|
||||
} else {
|
||||
panic!("user heap not initialized");
|
||||
};
|
||||
|
||||
if address == 0 {
|
||||
//println!("Brk query {:X}", current);
|
||||
Ok(current)
|
||||
} else if address >= arch::USER_HEAP_OFFSET {
|
||||
//TODO: out of memory errors
|
||||
if let Some(ref heap_shared) = context.heap {
|
||||
heap_shared.with(|heap| {
|
||||
heap.resize(address - arch::USER_HEAP_OFFSET, true);
|
||||
});
|
||||
} else {
|
||||
panic!("user heap not initialized");
|
||||
}
|
||||
|
||||
//println!("Brk resize {:X}", address);
|
||||
Ok(address)
|
||||
} else {
|
||||
//println!("Brk no mem");
|
||||
Err(Error::new(ENOMEM))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
|
||||
let ppid;
|
||||
let pid;
|
||||
{
|
||||
let ruid;
|
||||
let rgid;
|
||||
let rns;
|
||||
let euid;
|
||||
let egid;
|
||||
let ens;
|
||||
let mut cpu_id = None;
|
||||
let arch;
|
||||
let vfork;
|
||||
let mut kfx_option = None;
|
||||
let mut kstack_option = None;
|
||||
let mut offset = 0;
|
||||
let mut image = vec![];
|
||||
let mut heap_option = None;
|
||||
let mut stack_option = None;
|
||||
let mut tls_option = None;
|
||||
let grants;
|
||||
let name;
|
||||
let cwd;
|
||||
let env;
|
||||
let files;
|
||||
|
||||
// Copy from old process
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
ppid = context.id;
|
||||
ruid = context.ruid;
|
||||
rgid = context.rgid;
|
||||
rns = context.rns;
|
||||
euid = context.euid;
|
||||
egid = context.egid;
|
||||
ens = context.ens;
|
||||
|
||||
if flags & CLONE_VM == CLONE_VM {
|
||||
cpu_id = context.cpu_id;
|
||||
}
|
||||
|
||||
arch = context.arch.clone();
|
||||
|
||||
if let Some(ref fx) = context.kfx {
|
||||
let mut new_fx = unsafe { Box::from_raw(::alloc::heap::allocate(512, 16) as *mut [u8; 512]) };
|
||||
for (new_b, b) in new_fx.iter_mut().zip(fx.iter()) {
|
||||
*new_b = *b;
|
||||
}
|
||||
kfx_option = Some(new_fx);
|
||||
}
|
||||
|
||||
if let Some(ref stack) = context.kstack {
|
||||
offset = stack_base - stack.as_ptr() as usize - mem::size_of::<usize>(); // Add clone ret
|
||||
let mut new_stack = stack.clone();
|
||||
|
||||
unsafe {
|
||||
let func_ptr = new_stack.as_mut_ptr().offset(offset as isize);
|
||||
*(func_ptr as *mut usize) = arch::interrupt::syscall::clone_ret as usize;
|
||||
}
|
||||
|
||||
kstack_option = Some(new_stack);
|
||||
}
|
||||
|
||||
if flags & CLONE_VM == CLONE_VM {
|
||||
for memory_shared in context.image.iter() {
|
||||
image.push(memory_shared.clone());
|
||||
}
|
||||
|
||||
if let Some(ref heap_shared) = context.heap {
|
||||
heap_option = Some(heap_shared.clone());
|
||||
}
|
||||
} else {
|
||||
for memory_shared in context.image.iter() {
|
||||
memory_shared.with(|memory| {
|
||||
let mut new_memory = context::memory::Memory::new(
|
||||
VirtualAddress::new(memory.start_address().get() + arch::USER_TMP_OFFSET),
|
||||
memory.size(),
|
||||
entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE,
|
||||
false
|
||||
);
|
||||
|
||||
unsafe {
|
||||
intrinsics::copy(memory.start_address().get() as *const u8,
|
||||
new_memory.start_address().get() as *mut u8,
|
||||
memory.size());
|
||||
}
|
||||
|
||||
new_memory.remap(memory.flags());
|
||||
image.push(new_memory.to_shared());
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ref heap_shared) = context.heap {
|
||||
heap_shared.with(|heap| {
|
||||
let mut new_heap = context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_TMP_HEAP_OFFSET),
|
||||
heap.size(),
|
||||
entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE,
|
||||
false
|
||||
);
|
||||
|
||||
unsafe {
|
||||
intrinsics::copy(heap.start_address().get() as *const u8,
|
||||
new_heap.start_address().get() as *mut u8,
|
||||
heap.size());
|
||||
}
|
||||
|
||||
new_heap.remap(heap.flags());
|
||||
heap_option = Some(new_heap.to_shared());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref stack) = context.stack {
|
||||
let mut new_stack = context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_TMP_STACK_OFFSET),
|
||||
stack.size(),
|
||||
entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE,
|
||||
false
|
||||
);
|
||||
|
||||
unsafe {
|
||||
intrinsics::copy(stack.start_address().get() as *const u8,
|
||||
new_stack.start_address().get() as *mut u8,
|
||||
stack.size());
|
||||
}
|
||||
|
||||
new_stack.remap(stack.flags());
|
||||
stack_option = Some(new_stack);
|
||||
}
|
||||
|
||||
if let Some(ref tls) = context.tls {
|
||||
let mut new_tls = context::memory::Tls {
|
||||
master: tls.master,
|
||||
file_size: tls.file_size,
|
||||
mem: context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_TMP_TLS_OFFSET),
|
||||
tls.mem.size(),
|
||||
entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE,
|
||||
true
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
intrinsics::copy(tls.master.get() as *const u8,
|
||||
new_tls.mem.start_address().get() as *mut u8,
|
||||
tls.file_size);
|
||||
}
|
||||
|
||||
new_tls.mem.remap(tls.mem.flags());
|
||||
tls_option = Some(new_tls);
|
||||
}
|
||||
|
||||
if flags & CLONE_VM == CLONE_VM {
|
||||
grants = context.grants.clone();
|
||||
} else {
|
||||
grants = Arc::new(Mutex::new(Vec::new()));
|
||||
}
|
||||
|
||||
if flags & CLONE_VM == CLONE_VM {
|
||||
name = context.name.clone();
|
||||
} else {
|
||||
name = Arc::new(Mutex::new(context.name.lock().clone()));
|
||||
}
|
||||
|
||||
if flags & CLONE_FS == CLONE_FS {
|
||||
cwd = context.cwd.clone();
|
||||
} else {
|
||||
cwd = Arc::new(Mutex::new(context.cwd.lock().clone()));
|
||||
}
|
||||
|
||||
if flags & CLONE_VM == CLONE_VM {
|
||||
env = context.env.clone();
|
||||
} else {
|
||||
let mut new_env = BTreeMap::new();
|
||||
for item in context.env.lock().iter() {
|
||||
new_env.insert(item.0.clone(), Arc::new(Mutex::new(item.1.lock().clone())));
|
||||
}
|
||||
env = Arc::new(Mutex::new(new_env));
|
||||
}
|
||||
|
||||
if flags & CLONE_FILES == CLONE_FILES {
|
||||
files = context.files.clone();
|
||||
} else {
|
||||
files = Arc::new(Mutex::new(context.files.lock().clone()));
|
||||
}
|
||||
}
|
||||
|
||||
// If not cloning files, dup to get a new number from scheme
|
||||
// This has to be done outside the context lock to prevent deadlocks
|
||||
if flags & CLONE_FILES == 0 {
|
||||
for (_fd, mut file_option) in files.lock().iter_mut().enumerate() {
|
||||
let new_file_option = if let Some(file) = *file_option {
|
||||
let result = {
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
|
||||
scheme.clone()
|
||||
};
|
||||
let result = scheme.dup(file.number, b"clone");
|
||||
result
|
||||
};
|
||||
match result {
|
||||
Ok(new_number) => {
|
||||
Some(context::file::File {
|
||||
scheme: file.scheme,
|
||||
number: new_number,
|
||||
event: None,
|
||||
})
|
||||
},
|
||||
Err(_err) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
*file_option = new_file_option;
|
||||
}
|
||||
}
|
||||
|
||||
// If vfork, block the current process
|
||||
// This has to be done after the operations that may require context switches
|
||||
if flags & CLONE_VFORK == CLONE_VFORK {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context_lock.write();
|
||||
context.block();
|
||||
vfork = true;
|
||||
} else {
|
||||
vfork = false;
|
||||
}
|
||||
|
||||
// Set up new process
|
||||
{
|
||||
let mut contexts = context::contexts_mut();
|
||||
let context_lock = contexts.new_context()?;
|
||||
let mut context = context_lock.write();
|
||||
|
||||
pid = context.id;
|
||||
|
||||
context.ppid = ppid;
|
||||
context.ruid = ruid;
|
||||
context.rgid = rgid;
|
||||
context.rns = rns;
|
||||
context.euid = euid;
|
||||
context.egid = egid;
|
||||
context.ens = ens;
|
||||
|
||||
context.cpu_id = cpu_id;
|
||||
|
||||
context.status = context::Status::Runnable;
|
||||
|
||||
context.vfork = vfork;
|
||||
|
||||
context.arch = arch;
|
||||
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(arch::USER_TMP_MISC_OFFSET)));
|
||||
|
||||
let mut new_table = {
|
||||
let frame = allocate_frame().expect("no more frames in syscall::clone new_table");
|
||||
InactivePageTable::new(frame, &mut active_table, &mut temporary_page)
|
||||
};
|
||||
|
||||
context.arch.set_page_table(unsafe { new_table.address() });
|
||||
|
||||
// Copy kernel mapping
|
||||
{
|
||||
let frame = active_table.p4()[510].pointed_frame().expect("kernel table not mapped");
|
||||
let flags = active_table.p4()[510].flags();
|
||||
active_table.with(&mut new_table, &mut temporary_page, |mapper| {
|
||||
mapper.p4_mut()[510].set(frame, flags);
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(fx) = kfx_option.take() {
|
||||
context.arch.set_fx(fx.as_ptr() as usize);
|
||||
context.kfx = Some(fx);
|
||||
}
|
||||
|
||||
// Set kernel stack
|
||||
if let Some(stack) = kstack_option.take() {
|
||||
context.arch.set_stack(stack.as_ptr() as usize + offset);
|
||||
context.kstack = Some(stack);
|
||||
}
|
||||
|
||||
// Setup heap
|
||||
if flags & CLONE_VM == CLONE_VM {
|
||||
// Copy user image mapping, if found
|
||||
if ! image.is_empty() {
|
||||
let frame = active_table.p4()[0].pointed_frame().expect("user image not mapped");
|
||||
let flags = active_table.p4()[0].flags();
|
||||
active_table.with(&mut new_table, &mut temporary_page, |mapper| {
|
||||
mapper.p4_mut()[0].set(frame, flags);
|
||||
});
|
||||
}
|
||||
context.image = image;
|
||||
|
||||
// Copy user heap mapping, if found
|
||||
if let Some(heap_shared) = heap_option {
|
||||
let frame = active_table.p4()[1].pointed_frame().expect("user heap not mapped");
|
||||
let flags = active_table.p4()[1].flags();
|
||||
active_table.with(&mut new_table, &mut temporary_page, |mapper| {
|
||||
mapper.p4_mut()[1].set(frame, flags);
|
||||
});
|
||||
context.heap = Some(heap_shared);
|
||||
}
|
||||
|
||||
// Copy grant mapping
|
||||
if ! grants.lock().is_empty() {
|
||||
let frame = active_table.p4()[2].pointed_frame().expect("user grants not mapped");
|
||||
let flags = active_table.p4()[2].flags();
|
||||
active_table.with(&mut new_table, &mut temporary_page, |mapper| {
|
||||
mapper.p4_mut()[2].set(frame, flags);
|
||||
});
|
||||
}
|
||||
context.grants = grants;
|
||||
} else {
|
||||
// Copy percpu mapping
|
||||
for cpu_id in 0..::cpu_count() {
|
||||
extern {
|
||||
/// The starting byte of the thread data segment
|
||||
static mut __tdata_start: u8;
|
||||
/// The ending byte of the thread BSS segment
|
||||
static mut __tbss_end: u8;
|
||||
}
|
||||
|
||||
let size = unsafe { & __tbss_end as *const _ as usize - & __tdata_start as *const _ as usize };
|
||||
|
||||
let start = arch::KERNEL_PERCPU_OFFSET + arch::KERNEL_PERCPU_SIZE * cpu_id;
|
||||
let end = start + size;
|
||||
|
||||
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) {
|
||||
let frame = active_table.translate_page(page).expect("kernel percpu not mapped");
|
||||
active_table.with(&mut new_table, &mut temporary_page, |mapper| {
|
||||
let result = mapper.map_to(page, frame, entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE);
|
||||
// Ignore result due to operating on inactive table
|
||||
unsafe { result.ignore(); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Move copy of image
|
||||
for memory_shared in image.iter_mut() {
|
||||
memory_shared.with(|memory| {
|
||||
let start = VirtualAddress::new(memory.start_address().get() - arch::USER_TMP_OFFSET + arch::USER_OFFSET);
|
||||
memory.move_to(start, &mut new_table, &mut temporary_page);
|
||||
});
|
||||
}
|
||||
context.image = image;
|
||||
|
||||
// Move copy of heap
|
||||
if let Some(heap_shared) = heap_option {
|
||||
heap_shared.with(|heap| {
|
||||
heap.move_to(VirtualAddress::new(arch::USER_HEAP_OFFSET), &mut new_table, &mut temporary_page);
|
||||
});
|
||||
context.heap = Some(heap_shared);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup user stack
|
||||
if let Some(mut stack) = stack_option {
|
||||
stack.move_to(VirtualAddress::new(arch::USER_STACK_OFFSET), &mut new_table, &mut temporary_page);
|
||||
context.stack = Some(stack);
|
||||
}
|
||||
|
||||
// Setup user TLS
|
||||
if let Some(mut tls) = tls_option {
|
||||
tls.mem.move_to(VirtualAddress::new(arch::USER_TLS_OFFSET), &mut new_table, &mut temporary_page);
|
||||
context.tls = Some(tls);
|
||||
}
|
||||
|
||||
context.name = name;
|
||||
|
||||
context.cwd = cwd;
|
||||
|
||||
context.env = env;
|
||||
|
||||
context.files = files;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { context::switch(); }
|
||||
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
fn empty(context: &mut context::Context, reaping: bool) {
|
||||
if reaping {
|
||||
// Memory should already be unmapped
|
||||
assert!(context.image.is_empty());
|
||||
assert!(context.heap.is_none());
|
||||
assert!(context.stack.is_none());
|
||||
assert!(context.tls.is_none());
|
||||
} else {
|
||||
// Unmap previous image, heap, grants, stack, and tls
|
||||
context.image.clear();
|
||||
drop(context.heap.take());
|
||||
drop(context.stack.take());
|
||||
drop(context.tls.take());
|
||||
}
|
||||
|
||||
// FIXME: Looks like a race condition.
|
||||
// Is it possible for Arc::strong_count to return 1 to two contexts that exit at the
|
||||
// same time, or return 2 to both, thus either double freeing or leaking the grants?
|
||||
if Arc::strong_count(&context.grants) == 1 {
|
||||
let mut grants = context.grants.lock();
|
||||
for grant in grants.drain(..) {
|
||||
if reaping {
|
||||
println!("{}: {}: Grant should not exist: {:?}", context.id.into(), unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) }, grant);
|
||||
|
||||
let mut new_table = unsafe { InactivePageTable::from_address(context.arch.get_page_table()) };
|
||||
let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(arch::USER_TMP_GRANT_OFFSET)));
|
||||
|
||||
grant.unmap_inactive(&mut new_table, &mut temporary_page);
|
||||
} else {
|
||||
grant.unmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
|
||||
let entry;
|
||||
let mut sp = arch::USER_STACK_OFFSET + arch::USER_STACK_SIZE - 256;
|
||||
|
||||
{
|
||||
let mut args = Vec::new();
|
||||
for arg_ptr in arg_ptrs {
|
||||
let arg = validate_slice(arg_ptr[0] as *const u8, arg_ptr[1])?;
|
||||
args.push(arg.to_vec()); // Must be moved into kernel space before exec unmaps all memory
|
||||
}
|
||||
|
||||
let (uid, gid, canonical) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.euid, context.egid, context.canonicalize(path))
|
||||
};
|
||||
|
||||
let file = syscall::open(&canonical, syscall::flag::O_RDONLY)?;
|
||||
let mut stat = Stat::default();
|
||||
syscall::file_op_mut_slice(syscall::number::SYS_FSTAT, file, &mut stat)?;
|
||||
|
||||
let mut perm = stat.st_mode & 0o7;
|
||||
if stat.st_uid == uid {
|
||||
perm |= (stat.st_mode >> 6) & 0o7;
|
||||
}
|
||||
if stat.st_gid == gid {
|
||||
perm |= (stat.st_mode >> 3) & 0o7;
|
||||
}
|
||||
if uid == 0 {
|
||||
perm |= 0o7;
|
||||
}
|
||||
|
||||
if perm & 0o1 != 0o1 {
|
||||
let _ = syscall::close(file);
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
//TODO: Only read elf header, not entire file. Then read required segments
|
||||
let mut data = vec![0; stat.st_size as usize];
|
||||
syscall::file_op_mut_slice(syscall::number::SYS_READ, file, &mut data)?;
|
||||
let _ = syscall::close(file);
|
||||
|
||||
match elf::Elf::from(&data) {
|
||||
Ok(elf) => {
|
||||
entry = elf.entry();
|
||||
|
||||
drop(path); // Drop so that usage is not allowed after unmapping context
|
||||
drop(arg_ptrs); // Drop so that usage is not allowed after unmapping context
|
||||
|
||||
let contexts = context::contexts();
|
||||
let (vfork, ppid, files) = {
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context_lock.write();
|
||||
|
||||
// Set name
|
||||
context.name = Arc::new(Mutex::new(canonical));
|
||||
|
||||
empty(&mut context, false);
|
||||
|
||||
if stat.st_mode & syscall::flag::MODE_SETUID == syscall::flag::MODE_SETUID {
|
||||
context.euid = stat.st_uid;
|
||||
}
|
||||
|
||||
if stat.st_mode & syscall::flag::MODE_SETGID == syscall::flag::MODE_SETGID {
|
||||
context.egid = stat.st_gid;
|
||||
}
|
||||
|
||||
// Map and copy new segments
|
||||
let mut tls_option = None;
|
||||
for segment in elf.segments() {
|
||||
if segment.p_type == program_header::PT_LOAD {
|
||||
let mut memory = context::memory::Memory::new(
|
||||
VirtualAddress::new(segment.p_vaddr as usize),
|
||||
segment.p_memsz as usize,
|
||||
entry::NO_EXECUTE | entry::WRITABLE,
|
||||
true
|
||||
);
|
||||
|
||||
unsafe {
|
||||
// Copy file data
|
||||
intrinsics::copy((elf.data.as_ptr() as usize + segment.p_offset as usize) as *const u8,
|
||||
segment.p_vaddr as *mut u8,
|
||||
segment.p_filesz as usize);
|
||||
}
|
||||
|
||||
let mut flags = entry::NO_EXECUTE | entry::USER_ACCESSIBLE;
|
||||
|
||||
if segment.p_flags & program_header::PF_R == program_header::PF_R {
|
||||
flags.insert(entry::PRESENT);
|
||||
}
|
||||
|
||||
// W ^ X. If it is executable, do not allow it to be writable, even if requested
|
||||
if segment.p_flags & program_header::PF_X == program_header::PF_X {
|
||||
flags.remove(entry::NO_EXECUTE);
|
||||
} else if segment.p_flags & program_header::PF_W == program_header::PF_W {
|
||||
flags.insert(entry::WRITABLE);
|
||||
}
|
||||
|
||||
memory.remap(flags);
|
||||
|
||||
context.image.push(memory.to_shared());
|
||||
} else if segment.p_type == program_header::PT_TLS {
|
||||
let memory = context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_TCB_OFFSET),
|
||||
4096,
|
||||
entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE,
|
||||
true
|
||||
);
|
||||
|
||||
unsafe { *(arch::USER_TCB_OFFSET as *mut usize) = arch::USER_TLS_OFFSET + segment.p_memsz as usize; }
|
||||
|
||||
context.image.push(memory.to_shared());
|
||||
|
||||
tls_option = Some((
|
||||
VirtualAddress::new(segment.p_vaddr as usize),
|
||||
segment.p_filesz as usize,
|
||||
segment.p_memsz as usize
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Map heap
|
||||
context.heap = Some(context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_HEAP_OFFSET),
|
||||
0,
|
||||
entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE,
|
||||
true
|
||||
).to_shared());
|
||||
|
||||
// Map stack
|
||||
context.stack = Some(context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_STACK_OFFSET),
|
||||
arch::USER_STACK_SIZE,
|
||||
entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE,
|
||||
true
|
||||
));
|
||||
|
||||
// Map TLS
|
||||
if let Some((master, file_size, size)) = tls_option {
|
||||
let tls = context::memory::Tls {
|
||||
master: master,
|
||||
file_size: file_size,
|
||||
mem: context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_TLS_OFFSET),
|
||||
size,
|
||||
entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE,
|
||||
true
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// Copy file data
|
||||
intrinsics::copy(master.get() as *const u8,
|
||||
tls.mem.start_address().get() as *mut u8,
|
||||
file_size);
|
||||
}
|
||||
|
||||
context.tls = Some(tls);
|
||||
}
|
||||
|
||||
// Push arguments
|
||||
let mut arg_size = 0;
|
||||
for arg in args.iter().rev() {
|
||||
sp -= mem::size_of::<usize>();
|
||||
unsafe { *(sp as *mut usize) = arch::USER_ARG_OFFSET + arg_size; }
|
||||
sp -= mem::size_of::<usize>();
|
||||
unsafe { *(sp as *mut usize) = arg.len(); }
|
||||
|
||||
arg_size += arg.len();
|
||||
}
|
||||
|
||||
sp -= mem::size_of::<usize>();
|
||||
unsafe { *(sp as *mut usize) = args.len(); }
|
||||
|
||||
if arg_size > 0 {
|
||||
let mut memory = context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_ARG_OFFSET),
|
||||
arg_size,
|
||||
entry::NO_EXECUTE | entry::WRITABLE,
|
||||
true
|
||||
);
|
||||
|
||||
let mut arg_offset = 0;
|
||||
for arg in args.iter().rev() {
|
||||
unsafe {
|
||||
intrinsics::copy(arg.as_ptr(),
|
||||
(arch::USER_ARG_OFFSET + arg_offset) as *mut u8,
|
||||
arg.len());
|
||||
}
|
||||
|
||||
arg_offset += arg.len();
|
||||
}
|
||||
|
||||
memory.remap(entry::NO_EXECUTE | entry::USER_ACCESSIBLE);
|
||||
|
||||
context.image.push(memory.to_shared());
|
||||
}
|
||||
|
||||
let files = Arc::new(Mutex::new(context.files.lock().clone()));
|
||||
context.files = files.clone();
|
||||
|
||||
let vfork = context.vfork;
|
||||
context.vfork = false;
|
||||
(vfork, context.ppid, files)
|
||||
};
|
||||
|
||||
// Duplicate current files using b"exec", close previous
|
||||
for (fd, mut file_option) in files.lock().iter_mut().enumerate() {
|
||||
let new_file_option = if let Some(file) = *file_option {
|
||||
// Duplicate
|
||||
let result = {
|
||||
let scheme_option = {
|
||||
let schemes = scheme::schemes();
|
||||
schemes.get(file.scheme).map(|scheme| scheme.clone())
|
||||
};
|
||||
if let Some(scheme) = scheme_option {
|
||||
let result = scheme.dup(file.number, b"exec");
|
||||
result
|
||||
} else {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
};
|
||||
|
||||
// Close
|
||||
{
|
||||
if let Some(event_id) = file.event {
|
||||
context::event::unregister(FileHandle::from(fd), file.scheme, event_id);
|
||||
}
|
||||
|
||||
let scheme_option = {
|
||||
let schemes = scheme::schemes();
|
||||
schemes.get(file.scheme).map(|scheme| scheme.clone())
|
||||
};
|
||||
if let Some(scheme) = scheme_option {
|
||||
let _ = scheme.close(file.number);
|
||||
}
|
||||
}
|
||||
|
||||
// Return new descriptor
|
||||
match result {
|
||||
Ok(new_number) => {
|
||||
Some(context::file::File {
|
||||
scheme: file.scheme,
|
||||
number: new_number,
|
||||
event: None,
|
||||
})
|
||||
},
|
||||
Err(_err) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
*file_option = new_file_option;
|
||||
}
|
||||
|
||||
if vfork {
|
||||
if let Some(context_lock) = contexts.get(ppid) {
|
||||
let mut context = context_lock.write();
|
||||
if ! context.unblock() {
|
||||
println!("{:?} not blocked for exec vfork unblock", ppid);
|
||||
}
|
||||
} else {
|
||||
println!("{:?} not found for exec vfork unblock", ppid);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
println!("failed to execute {}: {}", unsafe { str::from_utf8_unchecked(path) }, err);
|
||||
return Err(Error::new(ENOEXEC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go to usermode
|
||||
unsafe { usermode(entry, sp); }
|
||||
}
|
||||
|
||||
pub fn exit(status: usize) -> ! {
|
||||
{
|
||||
let context_lock = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH)).expect("exit failed to find context");
|
||||
context_lock.clone()
|
||||
};
|
||||
|
||||
let mut close_files = Vec::new();
|
||||
let (pid, ppid) = {
|
||||
let mut context = context_lock.write();
|
||||
// FIXME: Looks like a race condition.
|
||||
// Is it possible for Arc::strong_count to return 1 to two contexts that exit at the
|
||||
// same time, or return 2 to both, thus either double closing or leaking the files?
|
||||
if Arc::strong_count(&context.files) == 1 {
|
||||
mem::swap(context.files.lock().deref_mut(), &mut close_files);
|
||||
}
|
||||
context.files = Arc::new(Mutex::new(Vec::new()));
|
||||
(context.id, context.ppid)
|
||||
};
|
||||
|
||||
/// Files must be closed while context is valid so that messages can be passed
|
||||
for (fd, file_option) in close_files.drain(..).enumerate() {
|
||||
if let Some(file) = file_option {
|
||||
if let Some(event_id) = file.event {
|
||||
context::event::unregister(FileHandle::from(fd), file.scheme, event_id);
|
||||
}
|
||||
|
||||
let scheme_option = {
|
||||
let schemes = scheme::schemes();
|
||||
schemes.get(file.scheme).map(|scheme| scheme.clone())
|
||||
};
|
||||
if let Some(scheme) = scheme_option {
|
||||
let _ = scheme.close(file.number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Transfer child processes to parent
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
for (_id, context_lock) in contexts.iter() {
|
||||
let mut context = context_lock.write();
|
||||
if context.ppid == pid {
|
||||
context.ppid = ppid;
|
||||
context.vfork = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (vfork, children) = {
|
||||
let mut context = context_lock.write();
|
||||
|
||||
empty(&mut context, false);
|
||||
|
||||
let vfork = context.vfork;
|
||||
context.vfork = false;
|
||||
|
||||
context.status = context::Status::Exited(status);
|
||||
|
||||
let children = context.waitpid.receive_all();
|
||||
|
||||
(vfork, children)
|
||||
};
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(parent_lock) = contexts.get(ppid) {
|
||||
let waitpid = {
|
||||
let mut parent = parent_lock.write();
|
||||
if vfork {
|
||||
if ! parent.unblock() {
|
||||
println!("{:?} not blocked for exit vfork unblock", ppid);
|
||||
}
|
||||
}
|
||||
parent.waitpid.clone()
|
||||
};
|
||||
|
||||
for (c_pid, c_status) in children {
|
||||
waitpid.send(c_pid, c_status);
|
||||
}
|
||||
waitpid.send(pid, status);
|
||||
} else {
|
||||
println!("{:?} not found for exit vfork unblock", ppid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { context::switch(); }
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
pub fn getpid() -> Result<ContextId> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
Ok(context.id)
|
||||
}
|
||||
|
||||
pub fn kill(pid: ContextId, sig: usize) -> Result<usize> {
|
||||
let (ruid, euid) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.ruid, context.euid)
|
||||
};
|
||||
|
||||
if sig > 0 && sig <= 0x7F {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context_lock.write();
|
||||
if euid == 0
|
||||
|| euid == context.ruid
|
||||
|| ruid == context.ruid
|
||||
{
|
||||
context.pending.push_back(sig as u8);
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EPERM))
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
fn reap(pid: ContextId) -> Result<ContextId> {
|
||||
// Spin until not running
|
||||
let mut running = false;
|
||||
while running {
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
running = context.running;
|
||||
}
|
||||
|
||||
arch::interrupt::pause();
|
||||
}
|
||||
|
||||
let mut contexts = context::contexts_mut();
|
||||
let context_lock = contexts.remove(pid).ok_or(Error::new(ESRCH))?;
|
||||
{
|
||||
let mut context = context_lock.write();
|
||||
empty(&mut context, true);
|
||||
}
|
||||
drop(context_lock);
|
||||
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: ContextId, status_ptr: usize, flags: usize) -> Result<ContextId> {
|
||||
let waitpid = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.waitpid.clone()
|
||||
};
|
||||
|
||||
let mut tmp = [0];
|
||||
let status_slice = if status_ptr != 0 {
|
||||
validate_slice_mut(status_ptr as *mut usize, 1)?
|
||||
} else {
|
||||
&mut tmp
|
||||
};
|
||||
|
||||
if pid.into() == 0 {
|
||||
if flags & WNOHANG == WNOHANG {
|
||||
if let Some((w_pid, status)) = waitpid.receive_any_nonblock() {
|
||||
status_slice[0] = status;
|
||||
reap(w_pid)
|
||||
} else {
|
||||
Ok(ContextId::from(0))
|
||||
}
|
||||
} else {
|
||||
let (w_pid, status) = waitpid.receive_any();
|
||||
status_slice[0] = status;
|
||||
reap(w_pid)
|
||||
}
|
||||
} else {
|
||||
if flags & WNOHANG == WNOHANG {
|
||||
if let Some(status) = waitpid.receive_nonblock(&pid) {
|
||||
status_slice[0] = status;
|
||||
reap(pid)
|
||||
} else {
|
||||
Ok(ContextId::from(0))
|
||||
}
|
||||
} else {
|
||||
let status = waitpid.receive(&pid);
|
||||
status_slice[0] = status;
|
||||
reap(pid)
|
||||
}
|
||||
}
|
||||
}
|
53
kernel/src/syscall/time.rs
Normal file
53
kernel/src/syscall/time.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use arch;
|
||||
use context;
|
||||
use syscall::data::TimeSpec;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC};
|
||||
|
||||
pub fn clock_gettime(clock: usize, time: &mut TimeSpec) -> Result<usize> {
|
||||
match clock {
|
||||
CLOCK_REALTIME => {
|
||||
let arch_time = arch::time::realtime();
|
||||
time.tv_sec = arch_time.0 as i64;
|
||||
time.tv_nsec = arch_time.1 as i32;
|
||||
Ok(0)
|
||||
},
|
||||
CLOCK_MONOTONIC => {
|
||||
let arch_time = arch::time::monotonic();
|
||||
time.tv_sec = arch_time.0 as i64;
|
||||
time.tv_nsec = arch_time.1 as i32;
|
||||
Ok(0)
|
||||
},
|
||||
_ => Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result<usize> {
|
||||
let start = arch::time::monotonic();
|
||||
let sum = start.1 + req.tv_nsec as u64;
|
||||
let end = (start.0 + req.tv_sec as u64 + sum / 1000000000, sum % 1000000000);
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context_lock.write();
|
||||
|
||||
context.wake = Some(end);
|
||||
context.block();
|
||||
}
|
||||
|
||||
unsafe { context::switch(); }
|
||||
|
||||
if let Some(mut rem) = rem_opt {
|
||||
//TODO let current = arch::time::monotonic();
|
||||
rem.tv_sec = 0;
|
||||
rem.tv_nsec = 0;
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn sched_yield() -> Result<usize> {
|
||||
unsafe { context::switch(); }
|
||||
Ok(0)
|
||||
}
|
44
kernel/src/syscall/validate.rs
Normal file
44
kernel/src/syscall/validate.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use core::{mem, slice};
|
||||
|
||||
use arch::paging::{ActivePageTable, Page, VirtualAddress, entry};
|
||||
use syscall::error::*;
|
||||
|
||||
fn validate(address: usize, size: usize, flags: entry::EntryFlags) -> Result<()> {
|
||||
let active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
let start_page = Page::containing_address(VirtualAddress::new(address));
|
||||
let end_page = Page::containing_address(VirtualAddress::new(address + size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
if let Some(page_flags) = active_table.translate_page_flags(page) {
|
||||
if ! page_flags.contains(flags) {
|
||||
//println!("{:X}: Not {:?}", page.start_address().get(), flags);
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
} else {
|
||||
//println!("{:X}: Not found", page.start_address().get());
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convert a pointer and length to slice, if valid
|
||||
pub fn validate_slice<T>(ptr: *const T, len: usize) -> Result<&'static [T]> {
|
||||
if len == 0 {
|
||||
Ok(&[])
|
||||
} else {
|
||||
validate(ptr as usize, len * mem::size_of::<T>(), entry::PRESENT /* TODO | entry::USER_ACCESSIBLE */)?;
|
||||
Ok(unsafe { slice::from_raw_parts(ptr, len) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a pointer and length to slice, if valid
|
||||
pub fn validate_slice_mut<T>(ptr: *mut T, len: usize) -> Result<&'static mut [T]> {
|
||||
if len == 0 {
|
||||
Ok(&mut [])
|
||||
} else {
|
||||
validate(ptr as usize, len * mem::size_of::<T>(), entry::PRESENT | entry::WRITABLE /* TODO | entry::USER_ACCESSIBLE */)?;
|
||||
Ok(unsafe { slice::from_raw_parts_mut(ptr, len) })
|
||||
}
|
||||
}
|
29
kernel/src/tests/mod.rs
Normal file
29
kernel/src/tests/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use syscall::{self, Error};
|
||||
|
||||
/// Test stdio
|
||||
#[test]
|
||||
fn stdio() {
|
||||
// Test opening stdin
|
||||
assert_eq!(syscall::open(b"debug:", 0), Ok(0));
|
||||
|
||||
// Test opening stdout
|
||||
assert_eq!(syscall::open(b"debug:", 0), Ok(1));
|
||||
|
||||
// Test opening stderr
|
||||
assert_eq!(syscall::open(b"debug:", 0), Ok(2));
|
||||
|
||||
// Test writing stdout
|
||||
let stdout_str = b"STDOUT";
|
||||
assert_eq!(syscall::write(1, stdout_str), Ok(stdout_str.len()));
|
||||
|
||||
// Test writing stderr
|
||||
let stderr_str = b"STDERR";
|
||||
assert_eq!(syscall::write(2, stderr_str), Ok(stderr_str.len()));
|
||||
}
|
||||
|
||||
/// Test that invalid reads/writes cause errors
|
||||
#[test]
|
||||
fn invalid_path() {
|
||||
assert_eq!(syscall::read(999, &mut []), Err(Error::new(EBADF)));
|
||||
assert_eq!(syscall::write(999, &[]), Err(Error::new(EBADF)));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue