From b5ff0aabd561c1befcc583aa0d7139fddabda27b Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jackpot51@gmail.com>
Date: Sun, 9 Jul 2017 21:34:38 -0600
Subject: [PATCH] WIP: Signal handling

---
 src/consts.rs             | 12 +++++--
 src/context/context.rs    | 18 +++++++---
 src/context/switch.rs     | 33 +++++++++++++----
 src/scheme/sys/context.rs |  3 ++
 src/start.rs              | 33 ++++++++---------
 src/syscall/mod.rs        | 25 +++++++++++--
 src/syscall/process.rs    | 74 +++++++++++++++++++++++++++++++--------
 7 files changed, 151 insertions(+), 47 deletions(-)

diff --git a/src/consts.rs b/src/consts.rs
index 149c9b45..c8acb394 100644
--- a/src/consts.rs
+++ b/src/consts.rs
@@ -46,8 +46,13 @@
     /// Size of user stack
     pub const USER_STACK_SIZE: usize = 1024 * 1024; // 1 MB
 
+    /// Offset to user sigstack
+    pub const USER_SIGSTACK_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
+    /// Size of user sigstack
+    pub const USER_SIGSTACK_SIZE: usize = 256 * 1024; // 256 KB
+
     /// Offset to user TLS
-    pub const USER_TLS_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
+    pub const USER_TLS_OFFSET: usize = USER_SIGSTACK_OFFSET + PML4_SIZE;
 
     /// Offset to user temporary image (used when cloning)
     pub const USER_TMP_OFFSET: usize = USER_TLS_OFFSET + PML4_SIZE;
@@ -61,8 +66,11 @@
     /// Offset to user temporary stack (used when cloning)
     pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_GRANT_OFFSET + PML4_SIZE;
 
+    /// Offset to user temporary sigstack (used when cloning)
+    pub const USER_TMP_SIGSTACK_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE;
+
     /// Offset to user temporary tls (used when cloning)
-    pub const USER_TMP_TLS_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE;
+    pub const USER_TMP_TLS_OFFSET: usize = USER_TMP_SIGSTACK_OFFSET + PML4_SIZE;
 
     /// Offset for usage in other temporary pages
     pub const USER_TMP_MISC_OFFSET: usize = USER_TMP_TLS_OFFSET + PML4_SIZE;
diff --git a/src/context/context.rs b/src/context/context.rs
index f35a5d02..a059614c 100644
--- a/src/context/context.rs
+++ b/src/context/context.rs
@@ -1,6 +1,7 @@
 use alloc::arc::Arc;
 use alloc::boxed::Box;
 use collections::{BTreeMap, Vec, VecDeque};
+use core::mem;
 use spin::Mutex;
 
 use context::arch;
@@ -8,7 +9,8 @@ use context::file::File;
 use context::memory::{Grant, Memory, SharedMemory, Tls};
 use device;
 use scheme::{SchemeNamespace, FileHandle};
-use syscall::data::Event;
+use syscall::data::{Event, SigAction};
+use syscall::flag::SIG_DFL;
 use sync::{WaitMap, WaitQueue};
 
 /// Unique identifier for a context (i.e. `pid`).
@@ -69,6 +71,8 @@ pub struct Context {
     pub heap: Option<SharedMemory>,
     /// User stack
     pub stack: Option<Memory>,
+    /// User signal stack
+    pub sigstack: Option<Memory>,
     /// User Thread local storage
     pub tls: Option<Tls>,
     /// User grants
@@ -83,8 +87,8 @@ pub struct Context {
     pub env: Arc<Mutex<BTreeMap<Box<[u8]>, Arc<Mutex<Vec<u8>>>>>>,
     /// The open files in the scheme
     pub files: Arc<Mutex<Vec<Option<File>>>>,
-    /// Singal handlers
-    pub handlers: Arc<Mutex<BTreeMap<u8, usize>>>,
+    /// Singal actions
+    pub actions: Arc<Mutex<Vec<SigAction>>>,
 }
 
 impl Context {
@@ -111,6 +115,7 @@ impl Context {
             image: Vec::new(),
             heap: None,
             stack: None,
+            sigstack: None,
             tls: None,
             grants: Arc::new(Mutex::new(Vec::new())),
             name: Arc::new(Mutex::new(Vec::new())),
@@ -118,7 +123,12 @@ impl Context {
             events: Arc::new(WaitQueue::new()),
             env: Arc::new(Mutex::new(BTreeMap::new())),
             files: Arc::new(Mutex::new(Vec::new())),
-            handlers: Arc::new(Mutex::new(BTreeMap::new())),
+            actions: Arc::new(Mutex::new(vec![SigAction {
+                sa_handler: unsafe { mem::transmute(SIG_DFL) },
+                sa_mask: [0; 2],
+                sa_flags: 0,
+                sa_restorer: unsafe { mem::transmute(0usize) },
+            }; 128])),
         }
     }
 
diff --git a/src/context/switch.rs b/src/context/switch.rs
index fd560c93..fdd05292 100644
--- a/src/context/switch.rs
+++ b/src/context/switch.rs
@@ -1,9 +1,12 @@
+use core::mem;
 use core::sync::atomic::Ordering;
 use context::{arch, contexts, Context, Status, CONTEXT_ID};
-use interrupt::irq::PIT_TICKS;
+use start::usermode;
+use syscall::flag::{SIG_DFL, SIG_IGN};
 
 use gdt;
 use interrupt;
+use interrupt::irq::PIT_TICKS;
 use syscall;
 use time;
 
@@ -118,19 +121,35 @@ pub unsafe fn switch() -> bool {
 }
 
 extern "C" fn signal_handler(sig: usize) {
-    let handler = {
+    let action = {
         let contexts = contexts();
         let context_lock = contexts.current().expect("context::signal_handler not inside of context");
         let context = context_lock.read();
-        let handlers = context.handlers.lock();
-        handlers.get(&(sig as u8)).map_or(0, |sig| *sig)
+        let actions = context.actions.lock();
+        actions[sig]
     };
 
-    println!("Signal handler: {}, {:X}", sig, handler);
+    println!("Signal handler: {:X}, {:?}", sig, action);
 
-    if handler == 0 {
+    let handler = action.sa_handler as usize;
+    let restorer = action.sa_restorer as usize;
+    if handler == SIG_DFL {
+        println!("Exit {:X}", sig);
         syscall::exit(sig);
+    } else if handler == SIG_IGN {
+        println!("Ignore");
     } else {
-        // TODO: Call handler
+        println!("Call {:X}", handler);
+
+        unsafe {
+            let mut sp = ::USER_SIGSTACK_OFFSET + ::USER_SIGSTACK_SIZE - 256;
+
+            sp = (sp / 16) * 16;
+
+            sp -= mem::size_of::<usize>();
+            *(sp as *mut usize) = restorer;
+
+            usermode(handler, sp, sig);
+        }
     }
 }
diff --git a/src/scheme/sys/context.rs b/src/scheme/sys/context.rs
index a28820dc..f0bec7e7 100644
--- a/src/scheme/sys/context.rs
+++ b/src/scheme/sys/context.rs
@@ -72,6 +72,9 @@ pub fn resource() -> Result<Vec<u8>> {
             if let Some(ref stack) = context.stack {
                 memory += stack.size();
             }
+            if let Some(ref sigstack) = context.sigstack {
+                memory += sigstack.size();
+            }
 
             let memory_string = if memory >= 1024 * 1024 * 1024 {
                 format!("{} GB", memory / 1024 / 1024 / 1024)
diff --git a/src/start.rs b/src/start.rs
index bf9dedac..faac4b42 100644
--- a/src/start.rs
+++ b/src/start.rs
@@ -156,25 +156,26 @@ pub unsafe extern fn kstart_ap(cpu_id: usize, bsp_table: usize, stack_start: usi
     kmain_ap(cpu_id);
 }
 
-pub unsafe fn usermode(ip: usize, sp: usize) -> ! {
+pub unsafe fn usermode(ip: usize, sp: usize, arg: usize) -> ! {
     // Go to usermode
-    asm!("mov ds, ax
-        mov es, ax
-        mov fs, bx
-        mov gs, ax
-        push rax
-        push rcx
-        push rdx
-        push rsi
-        push rdi
+    asm!("mov ds, r10d
+        mov es, r10d
+        mov fs, r11d
+        mov gs, r10d
+        push r10
+        push r12
+        push r13
+        push r14
+        push r15
         iretq"
         : // No output because it never returns
-        :   "{rax}"(gdt::GDT_USER_DATA << 3 | 3), // Data segment
-            "{rbx}"(gdt::GDT_USER_TLS << 3 | 3), // TLS segment
-            "{rcx}"(sp), // Stack pointer
-            "{rdx}"(3 << 12 | 1 << 9), // Flags - Set IOPL and interrupt enable flag
-            "{rsi}"(gdt::GDT_USER_CODE << 3 | 3), // Code segment
-            "{rdi}"(ip) // IP
+        :   "{r10}"(gdt::GDT_USER_DATA << 3 | 3), // Data segment
+            "{r11}"(gdt::GDT_USER_TLS << 3 | 3), // TLS segment
+            "{r12}"(sp), // Stack pointer
+            "{r13}"(3 << 12 | 1 << 9), // Flags - Set IOPL and interrupt enable flag
+            "{r14}"(gdt::GDT_USER_CODE << 3 | 3), // Code segment
+            "{r15}"(ip) // IP
+            "{rdi}"(arg) // Argument
         : // No clobers because it never returns
         : "intel", "volatile");
     unreachable!();
diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs
index f2d69b45..74c602ef 100644
--- a/src/syscall/mod.rs
+++ b/src/syscall/mod.rs
@@ -12,7 +12,7 @@ pub use self::process::*;
 pub use self::time::*;
 pub use self::validate::*;
 
-use self::data::TimeSpec;
+use self::data::{SigAction, TimeSpec};
 use self::error::{Error, Result, ENOSYS};
 use self::number::*;
 
@@ -70,7 +70,14 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
             },
             _ => 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_NANOSLEEP => nanosleep(
+                    validate_slice(b as *const TimeSpec, 1).map(|req| &req[0])?,
+                    if c == 0 {
+                        None
+                    } else {
+                        Some(validate_slice_mut(c as *mut TimeSpec, 1).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),
@@ -80,7 +87,6 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
                 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_SIGNAL => signal(b, c),
                 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),
@@ -95,6 +101,19 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize
                 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_SIGACTION => sigaction(
+                    b,
+                    if c == 0 {
+                        None
+                    } else {
+                        Some(validate_slice(c as *const SigAction, 1).map(|act| &act[0])?)
+                    },
+                    if d == 0 {
+                        None
+                    } else {
+                        Some(validate_slice_mut(d as *mut SigAction, 1).map(|oldact| &mut oldact[0])?)
+                    }
+                ),
                 SYS_PIPE2 => pipe2(validate_slice_mut(b as *mut usize, 2)?, c),
                 SYS_PHYSALLOC => physalloc(b),
                 SYS_PHYSFREE => physfree(b, c),
diff --git a/src/syscall/process.rs b/src/syscall/process.rs
index 2b5e426b..7fcc79d0 100644
--- a/src/syscall/process.rs
+++ b/src/syscall/process.rs
@@ -18,7 +18,7 @@ use context::ContextId;
 use elf::{self, program_header};
 use scheme::{self, FileHandle};
 use syscall;
-use syscall::data::Stat;
+use syscall::data::{SigAction, Stat};
 use syscall::error::*;
 use syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, O_CLOEXEC, SIG_DFL, WNOHANG};
 use syscall::validate::{validate_slice, validate_slice_mut};
@@ -79,13 +79,14 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
         let mut image = vec![];
         let mut heap_option = None;
         let mut stack_option = None;
+        let mut sigstack_option = None;
         let mut tls_option = None;
         let grants;
         let name;
         let cwd;
         let env;
         let files;
-        let handlers;
+        let actions;
 
         // Copy from old process
         {
@@ -195,6 +196,24 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
                 stack_option = Some(new_stack);
             }
 
+            if let Some(ref sigstack) = context.sigstack {
+                let mut new_sigstack = context::memory::Memory::new(
+                    VirtualAddress::new(::USER_TMP_SIGSTACK_OFFSET),
+                    sigstack.size(),
+                    entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE,
+                    false
+                );
+
+                unsafe {
+                    intrinsics::copy(sigstack.start_address().get() as *const u8,
+                                    new_sigstack.start_address().get() as *mut u8,
+                                    sigstack.size());
+                }
+
+                new_sigstack.remap(sigstack.flags());
+                sigstack_option = Some(new_sigstack);
+            }
+
             if let Some(ref tls) = context.tls {
                 let mut new_tls = context::memory::Tls {
                     master: tls.master,
@@ -252,9 +271,9 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
             }
 
             if flags & CLONE_SIGHAND == CLONE_SIGHAND {
-                handlers = context.handlers.clone();
+                actions = context.actions.clone();
             } else {
-                handlers = Arc::new(Mutex::new(context.handlers.lock().clone()));
+                actions = Arc::new(Mutex::new(context.actions.lock().clone()));
             }
         }
 
@@ -441,6 +460,12 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
                 context.stack = Some(stack);
             }
 
+            // Setup user sigstack
+            if let Some(mut sigstack) = sigstack_option {
+                sigstack.move_to(VirtualAddress::new(::USER_SIGSTACK_OFFSET), &mut new_table, &mut temporary_page);
+                context.sigstack = Some(sigstack);
+            }
+
             // Setup user TLS
             if let Some(mut tls) = tls_option {
                 tls.mem.move_to(VirtualAddress::new(::USER_TLS_OFFSET), &mut new_table, &mut temporary_page);
@@ -455,7 +480,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
 
             context.files = files;
 
-            context.handlers = handlers;
+            context.actions = actions;
         }
     }
 
@@ -470,12 +495,14 @@ fn empty(context: &mut context::Context, reaping: bool) {
         assert!(context.image.is_empty());
         assert!(context.heap.is_none());
         assert!(context.stack.is_none());
+        assert!(context.sigstack.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.sigstack.take());
         drop(context.tls.take());
     }
 
@@ -673,6 +700,14 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
                         true
                     ));
 
+                    // Map stack
+                    context.sigstack = Some(context::memory::Memory::new(
+                        VirtualAddress::new(::USER_SIGSTACK_OFFSET),
+                        ::USER_SIGSTACK_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 {
@@ -737,7 +772,12 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
                     let files = Arc::new(Mutex::new(context.files.lock().clone()));
                     context.files = files.clone();
 
-                    context.handlers = Arc::new(Mutex::new(BTreeMap::new()));
+                    context.actions = Arc::new(Mutex::new(vec![SigAction {
+                        sa_handler: unsafe { mem::transmute(SIG_DFL) },
+                        sa_mask: [0; 2],
+                        sa_flags: 0,
+                        sa_restorer: unsafe { mem::transmute(0usize) },
+                    }; 128]));
 
                     let vfork = context.vfork;
                     context.vfork = false;
@@ -819,7 +859,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
     }
 
     // Go to usermode
-    unsafe { usermode(entry, sp); }
+    unsafe { usermode(entry, sp, 0); }
 }
 
 pub fn exit(status: usize) -> ! {
@@ -971,18 +1011,22 @@ pub fn kill(pid: ContextId, sig: usize) -> Result<usize> {
     }
 }
 
-pub fn signal(sig: usize, handler: usize) -> Result<usize> {
+pub fn sigaction(sig: usize, act_opt: Option<&SigAction>, oldact_opt: Option<&mut SigAction>) -> Result<usize> {
     if sig > 0 && sig <= 0x7F {
         let contexts = context::contexts();
         let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
-        let mut handlers = context.handlers.lock();
-        let previous = if handler == SIG_DFL {
-            handlers.remove(&(sig as u8))
-        } else {
-            handlers.insert(sig as u8, handler)
-        };
-        Ok(previous.unwrap_or(0))
+        let mut actions = context.actions.lock();
+
+        if let Some(oldact) = oldact_opt {
+            *oldact = actions[sig];
+        }
+
+        if let Some(act) = act_opt {
+            actions[sig] = *act;
+        }
+
+        Ok(0)
     } else {
         Err(Error::new(EINVAL))
     }
-- 
GitLab