From 2f06e18743a3da58b154779d92799e61101edbac Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jackpot51@gmail.com>
Date: Mon, 19 Sep 2016 21:24:54 -0600
Subject: [PATCH] WIP: User scheme

---
 scheme/mod.rs    |   2 +-
 scheme/user.rs   | 271 ++++++++++++++++-------------------------------
 syscall/call.rs  |  39 ++++---
 syscall/error.rs |  29 +++++
 syscall/mod.rs   |   6 +-
 5 files changed, 141 insertions(+), 206 deletions(-)

diff --git a/scheme/mod.rs b/scheme/mod.rs
index 41e79781..f4340a06 100644
--- a/scheme/mod.rs
+++ b/scheme/mod.rs
@@ -33,7 +33,7 @@ pub mod initfs;
 pub mod irq;
 
 /// Userspace schemes
-//pub mod user;
+pub mod user;
 
 /// Limit on number of schemes
 pub const SCHEME_MAX_SCHEMES: usize = 65536;
diff --git a/scheme/user.rs b/scheme/user.rs
index 23b9c719..6ff446ac 100644
--- a/scheme/user.rs
+++ b/scheme/user.rs
@@ -1,217 +1,126 @@
-use alloc::arc::{Arc, Weak};
-use alloc::boxed::Box;
-
 use collections::{BTreeMap, VecDeque};
+use core::sync::atomic::{AtomicUsize, Ordering};
+use core::{mem, usize};
+use spin::RwLock;
 
-use core::cell::Cell;
-use core::mem::size_of;
-use core::ops::DerefMut;
-use core::{ptr, slice};
-
-use syscall::{Call, Error, Result};
+use context;
+use syscall::{convert_to_result, Call, Error, Result};
 
 use super::Scheme;
 
+#[derive(Copy, Clone, Debug)]
+#[repr(packed)]
+pub struct Packet {
+    pub id: usize,
+    pub a: usize,
+    pub b: usize,
+    pub c: usize,
+    pub d: usize
+}
+
 /// UserScheme has to be wrapped
 pub struct UserScheme {
-    next_id: Cell<usize>,
-    todo: VecDeque<(usize, (usize, usize, usize, usize))>,
-    done: BTreeMap<usize, (usize, usize, usize, usize)>,
+    next_id: AtomicUsize,
+    todo: RwLock<VecDeque<Packet>>,
+    done: RwLock<BTreeMap<usize, usize>>
 }
 
 impl UserScheme {
     fn call(&self, a: Call, b: usize, c: usize, d: usize) -> Result<usize> {
-        let id = self.next_id.get();
-
-        //TODO: What should be done about collisions in self.todo or self.done?
-        let mut next_id = id + 1;
-        if next_id <= 0 {
-            next_id = 1;
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
+
+        self.todo.write().push_back(Packet {
+            id: id,
+            a: a as usize,
+            b: b,
+            c: c,
+            d: d
+        });
+
+        loop {
+            if let Some(a) = self.done.write().remove(&id) {
+                return convert_to_result(a);
+            }
+
+            unsafe { context::switch(); }
         }
-        self.next_id.set(next_id);
-
-        // println!("{} {}: {} {} {:X} {:X} {:X}", UserScheme.name, id, a, ::syscall::name(a), b, c, d);
-
-        Ok(0)
-    }
-
-    fn capture(&self, mut physical_address: usize, size: usize, writeable: bool) -> Result<usize> {
-        Ok(0)
-    }
-
-    fn release(&self, virtual_address: usize) {
-
     }
 }
 
 impl Scheme for UserScheme {
-    fn open(&mut self, path: &[u8], flags: usize) -> Result<usize> {
-        let virtual_address = try!(self.capture(path.as_ptr() as usize, path.len(), false));
-
-        let result = self.call(Call::Open, virtual_address, path.len(), flags);
-
-        self.release(virtual_address);
-
-        result
+    fn open(&self, path: &[u8], flags: usize) -> Result<usize> {
+        self.call(Call::Open, path.as_ptr() as usize, path.len(), flags)
     }
 
-    /*
-    fn mkdir(&mut self, path: &str, flags: usize) -> Result<()> {
-        let virtual_address = try!(self.capture(path.as_ptr() as usize, path.len(), false));
-
-        let result = self.call(Call::MkDir, virtual_address, path.len(), flags);
-
-        self.release(virtual_address);
-
-        result.and(Ok(()))
-    }
-
-    fn rmdir(&mut self, path: &str) -> Result<()> {
-        let virtual_address = try!(self.capture(path.as_ptr() as usize, path.len(), false));
-
-        let result = self.call(SYS_RMDIR, virtual_address, path.len(), 0);
-
-        self.release(virtual_address);
-
-        result.and(Ok(()))
-    }
-
-    fn unlink(&mut self, path: &str) -> Result<()> {
-        let virtual_address = try!(self.capture(path.as_ptr() as usize, path.len(), false));
-
-        let result = self.call(SYS_UNLINK, virtual_address, path.len(), 0);
-
-        self.release(virtual_address);
-
-        result.and(Ok(()))
-    }
-    */
-
-    /// Duplicate the resource
-    fn dup(&mut self, file: usize) -> Result<usize> {
-        self.call(Call::Dup, file, 0, 0)
-    }
-
-    /*
-    /// Return the URL of this resource
-    fn path(&self, file: usize, buf: &mut [u8]) -> Result <usize> {
-        let contexts = unsafe { & *::env().contexts.get() };
-        let current = try!(contexts.current());
-        if let Ok(physical_address) = current.translate(buf.as_mut_ptr() as usize, buf.len()) {
-            let offset = physical_address % 4096;
-
-            let virtual_address = try!(self.capture(physical_address - offset, buf.len() + offset, true));
-
-            let result = self.call(SYS_FPATH, file, virtual_address + offset, buf.len());
-
-            //println!("Read {:X} mapped from {:X} to {:X} offset {} length {} size {} result {:?}", physical_address, buf.as_ptr() as usize, virtual_address + offset, offset, buf.len(), virtual_size, result);
-
-            self.release(virtual_address);
-
-            result
+    fn dup(&self, file: usize) -> Result<usize> {
+        if file == usize::MAX {
+            Ok(file)
         } else {
-            println!("{}:{} fault {:X} {}", file!(), line!(), buf.as_ptr() as usize, buf.len());
-            Err(Error::Fault)
+            self.call(Call::Dup, file, 0, 0)
         }
     }
-    */
-
-    /// Read data to buffer
-    fn read(&mut self, file: usize, buf: &mut [u8]) -> Result<usize> {
-        /*
-        let contexts = unsafe { & *::env().contexts.get() };
-        let current = try!(contexts.current());
-        if let Ok(physical_address) = current.translate(buf.as_mut_ptr() as usize, buf.len()) {
-            let offset = physical_address % 4096;
-
-            let virtual_address = try!(self.capture(physical_address - offset, buf.len() + offset, true));
 
-            let result = self.call(Call::Read, file, virtual_address + offset, buf.len());
-
-            //println!("Read {:X} mapped from {:X} to {:X} offset {} length {} size {} result {:?}", physical_address, buf.as_ptr() as usize, virtual_address + offset, offset, buf.len(), virtual_size, result);
-
-            self.release(virtual_address);
-
-            result
-        } else */
-        {
-            println!("{}:{} fault {:X} {}", file!(), line!(), buf.as_ptr() as usize, buf.len());
-            Err(Error::Fault)
+    fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
+        if file == usize::MAX {
+            let packet_size = mem::size_of::<Packet>();
+            let len = buf.len()/packet_size;
+            if len > 0 {
+                loop {
+                    let mut i = 0;
+                    {
+                        let mut todo = self.todo.write();
+                        while ! todo.is_empty() && i < len {
+                            unsafe { *(buf.as_mut_ptr() as *mut Packet).offset(i as isize) = todo.pop_front().unwrap(); }
+                            i += 1;
+                        }
+                    }
+
+                    if i > 0 {
+                        return Ok(i * packet_size);
+                    } else {
+                        unsafe { context::switch(); }
+                    }
+                }
+            } else {
+                Ok(0)
+            }
+        } else {
+            self.call(Call::Read, file, buf.as_mut_ptr() as usize, buf.len())
         }
     }
 
-    /// Write to resource
-    fn write(&mut self, file: usize, buf: &[u8]) -> Result<usize> {
-        /*
-        let contexts = unsafe { & *::env().contexts.get() };
-        let current = try!(contexts.current());
-        if let Ok(physical_address) = current.translate(buf.as_ptr() as usize, buf.len()) {
-            let offset = physical_address % 4096;
-
-            let virtual_address = try!(self.capture(physical_address - offset, buf.len() + offset, false));
-
-            let result = self.call(Call::Write, file, virtual_address + offset, buf.len());
-
-            // println!("Write {:X} mapped from {:X} to {:X} offset {} length {} result {:?}", physical_address, buf.as_ptr() as usize, virtual_address + offset, offset, buf.len(), result);
+    fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
+        if file == usize::MAX {
+            let packet_size = mem::size_of::<Packet>();
+            let len = buf.len()/packet_size;
+            let mut i = 0;
+            while i < len {
+                let packet = unsafe { *(buf.as_ptr() as *const Packet).offset(i as isize) };
+                self.done.write().insert(packet.id, packet.a);
 
-            self.release(virtual_address);
+                i += 1;
+            }
 
-            result
-        } else */
-        {
-            println!("{}:{} fault {:X} {}", file!(), line!(), buf.as_ptr() as usize, buf.len());
-            Err(Error::Fault)
+            Ok(i * packet_size)
+        } else {
+            self.call(Call::Write, file, buf.as_ptr() as usize, buf.len())
         }
     }
 
-    /*
-    /// Seek
-    fn seek(&mut self, file: usize, pos: ResourceSeek) -> Result<usize> {
-        let (whence, offset) = match pos {
-            ResourceSeek::Start(offset) => (SEEK_SET, offset as usize),
-            ResourceSeek::Current(offset) => (SEEK_CUR, offset as usize),
-            ResourceSeek::End(offset) => (SEEK_END, offset as usize)
-        };
-
-        self.call(SYS_LSEEK, file, offset, whence)
-    }
-
-    /// Stat the resource
-    fn stat(&self, file: usize, stat: &mut Stat) -> Result<()> {
-        let buf = unsafe { slice::from_raw_parts_mut(stat as *mut Stat as *mut u8, size_of::<Stat>()) };
-
-        let contexts = unsafe { & *::env().contexts.get() };
-        let current = try!(contexts.current());
-        if let Ok(physical_address) = current.translate(buf.as_mut_ptr() as usize, buf.len()) {
-            let offset = physical_address % 4096;
-
-            let virtual_address = try!(self.capture(physical_address - offset, buf.len() + offset, true));
-
-            let result = self.call(SYS_FSTAT, file, virtual_address + offset, 0);
-
-            self.release(virtual_address);
-
-            result.and(Ok(()))
+    fn fsync(&self, file: usize) -> Result<()> {
+        if file == usize::MAX {
+            Ok(())
         } else {
-            println!("{}:{} fault {:X} {}", file!(), line!(), buf.as_ptr() as usize, buf.len());
-            Err(Error::Fault)
+            self.call(Call::FSync, file, 0, 0).and(Ok(()))
         }
     }
-    */
-
-    /// Sync the resource
-    fn fsync(&mut self, file: usize) -> Result<()> {
-        self.call(Call::FSync, file, 0, 0).and(Ok(()))
-    }
 
-    /*
-    /// Truncate the resource
-    fn truncate(&mut self, file: usize, len: usize) -> Result<()> {
-        self.call(SYS_FTRUNCATE, file, len, 0).and(Ok(()))
-    }
-    */
-
-    fn close(&mut self, file: usize) -> Result<()> {
-        self.call(Call::Close, file, 0, 0).and(Ok(()))
+    fn close(&self, file: usize) -> Result<()> {
+        if file == usize::MAX {
+            println!("Close user scheme");
+            Ok(())
+        } else {
+            self.call(Call::Close, file, 0, 0).and(Ok(()))
+        }
     }
 }
diff --git a/syscall/call.rs b/syscall/call.rs
index 3bcefd48..2aa41ae8 100644
--- a/syscall/call.rs
+++ b/syscall/call.rs
@@ -1,5 +1,3 @@
-use super::{Error, Result};
-
 /// System call list
 /// See http://syscalls.kernelgrok.com/ for numbers
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -42,26 +40,25 @@ pub enum Call {
 /// Convert numbers to calls
 /// See http://syscalls.kernelgrok.com/
 impl Call {
-    //TODO: Return Option<Call>
-    pub fn from(number: usize) -> Result<Call> {
+    pub fn from(number: usize) -> Option<Call> {
         match number {
-            1 => Ok(Call::Exit),
-            3 => Ok(Call::Read),
-            4 => Ok(Call::Write),
-            5 => Ok(Call::Open),
-            6 => Ok(Call::Close),
-            7 => Ok(Call::WaitPid),
-            11 => Ok(Call::Exec),
-            12 => Ok(Call::ChDir),
-            20 => Ok(Call::GetPid),
-            41 => Ok(Call::Dup),
-            45 => Ok(Call::Brk),
-            110 => Ok(Call::Iopl),
-            118 => Ok(Call::FSync),
-            120 => Ok(Call::Clone),
-            158 => Ok(Call::SchedYield),
-            183 => Ok(Call::GetCwd),
-            _ => Err(Error::NoCall)
+            1 => Some(Call::Exit),
+            3 => Some(Call::Read),
+            4 => Some(Call::Write),
+            5 => Some(Call::Open),
+            6 => Some(Call::Close),
+            7 => Some(Call::WaitPid),
+            11 => Some(Call::Exec),
+            12 => Some(Call::ChDir),
+            20 => Some(Call::GetPid),
+            41 => Some(Call::Dup),
+            45 => Some(Call::Brk),
+            110 => Some(Call::Iopl),
+            118 => Some(Call::FSync),
+            120 => Some(Call::Clone),
+            158 => Some(Call::SchedYield),
+            183 => Some(Call::GetCwd),
+            _ => None
         }
     }
 }
diff --git a/syscall/error.rs b/syscall/error.rs
index e71a8ca8..8fcffea3 100644
--- a/syscall/error.rs
+++ b/syscall/error.rs
@@ -31,4 +31,33 @@ pub enum Error {
     NoCall = 38
 }
 
+impl Error {
+    pub fn from(number: usize) -> Option<Error> {
+        match number {
+            1 => Some(Error::NotPermitted),
+            2 => Some(Error::NoEntry),
+            3 => Some(Error::NoProcess),
+            8 => Some(Error::NoExec),
+            9 => Some(Error::BadFile),
+            11 => Some(Error::TryAgain),
+            14 => Some(Error::Fault),
+            17 => Some(Error::FileExists),
+            19 => Some(Error::NoDevice),
+            22 => Some(Error::InvalidValue),
+            24 => Some(Error::TooManyFiles),
+            29 => Some(Error::IllegalSeek),
+            38 => Some(Error::NoCall),
+            _ => None
+        }
+    }
+}
+
 pub type Result<T> = ::core::result::Result<T, Error>;
+
+pub fn convert_to_result(number: usize) -> Result<usize> {
+    if let Some(err) = Error::from((-(number as isize)) as usize) {
+        Err(err)
+    } else {
+        Ok(number)
+    }
+}
diff --git a/syscall/mod.rs b/syscall/mod.rs
index 396f366a..b76dfbdc 100644
--- a/syscall/mod.rs
+++ b/syscall/mod.rs
@@ -26,7 +26,7 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
     #[inline(always)]
     fn inner(a: usize, b: usize, c: usize, d: usize, e: usize, _f: usize, stack: usize) -> Result<usize> {
         match Call::from(a) {
-            Ok(call) => match call {
+            Some(call) => match call {
                 Call::Exit => exit(b),
                 Call::Read => read(b, validate_slice_mut(c as *mut u8, d)?),
                 Call::Write => write(b, validate_slice(c as *const u8, d)?),
@@ -44,9 +44,9 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
                 Call::SchedYield => sched_yield(),
                 Call::GetCwd => getcwd(validate_slice_mut(b as *mut u8, c)?)
             },
-            Err(err) => {
+            None => {
                 println!("Unknown syscall {}", a);
-                Err(err)
+                Err(Error::NoCall)
             }
         }
     }
-- 
GitLab