From 3d2298c281bf365243002ad0a86aa153199c6613 Mon Sep 17 00:00:00 2001 From: S A Date: Sat, 26 Apr 2025 17:31:59 -0500 Subject: [PATCH 1/3] initial work on signals --- crates/kernel/src/process.rs | 5 ++++ crates/kernel/src/process/mem.rs | 13 ++++++++ crates/kernel/src/process/signal.rs | 26 ++++++++++++++++ crates/kernel/src/syscall/mod.rs | 6 ++++ crates/kernel/src/syscall/signal.rs | 46 +++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 crates/kernel/src/process/signal.rs create mode 100644 crates/kernel/src/syscall/signal.rs diff --git a/crates/kernel/src/process.rs b/crates/kernel/src/process.rs index 890f0191..1904561a 100644 --- a/crates/kernel/src/process.rs +++ b/crates/kernel/src/process.rs @@ -6,6 +6,7 @@ use crate::sync::SpinLock; pub mod fd; pub mod mem; +pub mod signal; pub type ProcessRef = Arc; @@ -22,6 +23,8 @@ pub struct Process { pub root: Option, pub file_descriptors: SpinLock, pub exit_code: Arc>, + pub signal_handlers: signal::SignalHandlers, + pub signal_flags: signal::SignalFlags, // TODO: replace this with signal queue } impl Process { @@ -33,6 +36,8 @@ impl Process { root: None, file_descriptors: SpinLock::new(FileDescriptorList { desc: Vec::new() }), exit_code: Arc::new(BlockingOnceCell::new()), + signal_handlers: signal::SignalHandlers::new(), + signal_flags: signal::SignalFlags::empty(), } } diff --git a/crates/kernel/src/process/mem.rs b/crates/kernel/src/process/mem.rs index 313821c4..84d5d7fa 100644 --- a/crates/kernel/src/process/mem.rs +++ b/crates/kernel/src/process/mem.rs @@ -11,9 +11,11 @@ use crate::arch::memory::vmm::{ use crate::event::async_handler::{run_async_handler, HandlerContext}; use crate::event::context::Context; use crate::event::exceptions::DataAbortISS; +use crate::process::signal::SignalFlags; use crate::syscall::proc::exit_user_thread; use super::fd::ArcFd; +use super::signal; #[derive(Debug)] pub enum MmapError { @@ -310,6 +312,10 @@ unsafe impl Sync for UserAddrSpace {} pub fn page_fault_handler(ctx: &mut Context, far: usize, _iss: DataAbortISS) -> *mut Context { run_async_handler(ctx, async move |mut context: HandlerContext<'_>| { let proc = context.cur_process().unwrap(); + + if proc.signal_flags.contains(signal::SignalFlags::IN_HANDLER) { + println!("Page fault at addr {far:#10x} while in a signal handler"); + } // TODO: make sure misaligned loads don't loop here? let page_addr = (far / PAGE_SIZE) * PAGE_SIZE; @@ -318,6 +324,13 @@ pub fn page_fault_handler(ctx: &mut Context, far: usize, _iss: DataAbortISS) -> let vme = mem.get_vme(page_addr); match vme { None => { + + if let Some(user_page_fault_handler) = proc.signal_handlers.user_page_fault_handler { + proc.signal_flags.set(SignalFlags::IN_HANDLER, true); + //TODO: run page fault handler and plug in correct args + proc.signal_flags.set(SignalFlags::IN_HANDLER, false); + } + let exit_code = &proc.exit_code; exit_code.set(crate::process::ExitStatus { status: -1i32 as u32, diff --git a/crates/kernel/src/process/signal.rs b/crates/kernel/src/process/signal.rs new file mode 100644 index 00000000..c95e477c --- /dev/null +++ b/crates/kernel/src/process/signal.rs @@ -0,0 +1,26 @@ + + +#[derive(Debug)] +pub struct SignalHandlers { + pub user_page_fault_handler: Option, + pub kill_block_handler: Option, +} + +//TODO: replace this with a queue +bitflags::bitflags! { + + #[derive(Clone, Copy, Debug)] + pub struct SignalFlags: u8 { + const IS_KILL = 1 << 0; //can block this + const IS_DEAD = 1 << 1; //can't block this + const IN_HANDLER = 1 << 2; //currently not supporting nested signal handlers + } +} + +impl SignalHandlers { + pub fn new() -> SignalHandlers { + SignalHandlers { user_page_fault_handler: None, kill_block_handler: None } + } + +} + diff --git a/crates/kernel/src/syscall/mod.rs b/crates/kernel/src/syscall/mod.rs index 311e4ac8..10e8e2aa 100644 --- a/crates/kernel/src/syscall/mod.rs +++ b/crates/kernel/src/syscall/mod.rs @@ -10,6 +10,7 @@ pub mod proc; pub mod semaphore; pub mod sync; pub mod time; +pub mod signal; pub unsafe fn register_syscalls() { unsafe { @@ -43,5 +44,10 @@ pub unsafe fn register_syscalls() { register_syscall_handler(26, semaphore::sys_sem_create); register_syscall_handler(27, semaphore::sys_sem_up); register_syscall_handler(28, semaphore::sys_sem_down); + + register_syscall_handler(33, signal::sys_register_user_page_fault_handler); + register_syscall_handler(34, signal::sys_register_kill_block_handler); + register_syscall_handler(35, signal::sys_kill); + register_syscall_handler(36, signal::sys_kill_unblockable); } } diff --git a/crates/kernel/src/syscall/signal.rs b/crates/kernel/src/syscall/signal.rs new file mode 100644 index 00000000..f04e14c5 --- /dev/null +++ b/crates/kernel/src/syscall/signal.rs @@ -0,0 +1,46 @@ +use crate::event::async_handler::{run_async_handler, HandlerContext}; +use crate::event::context::{deschedule_thread, Context, DescheduleAction, CORES}; +use crate::process::ExitStatus; + +//TODO: register these syscalls + +//Users should be able to redefine these? + +///Register a function to run when the user process encounters a page fault +pub unsafe fn sys_register_user_page_fault_handler(ctx: &mut Context) -> *mut Context { + let user_page_fault_handler: fn() = unsafe { core::mem::transmute::(ctx.regs[0]) }; + //TODO: add safety checks on this pointer + + run_async_handler(ctx, async move |mut context: HandlerContext<'_>| { + let proc = context.cur_process().unwrap(); + + proc.signal_handlers.user_page_fault_handler = Some(user_page_fault_handler); + + context.resume_return(0) + }) +} + +///Register a function to run when the process receives a kill signal +pub unsafe fn sys_register_kill_block_handler(ctx: &mut Context) -> *mut Context { + let user_kill_block_handler: fn() = unsafe { core::mem::transmute::(ctx.regs[0]) }; + //TODO: add safety checks on this pointer + + run_async_handler(ctx, async move |mut context: HandlerContext<'_>| { + let proc = context.cur_process().unwrap(); + + proc.signal_handlers.kill_block_handler = Some(user_kill_block_handler); + + context.resume_return(0) + }) +} + +///Kill which can be blocked if the user registers a handler +pub unsafe fn sys_kill(ctx: &mut Context) -> *mut Context { + todo!(); +} + +///Kill which cannot be blocked +pub unsafe fn sys_kill_unblockable(ctx: &mut Context) -> *mut Context { + todo!(); +} + From b035f0a655ae5a358038e418b2e080e733cd63f3 Mon Sep 17 00:00:00 2001 From: S A Date: Fri, 2 May 2025 00:11:00 -0500 Subject: [PATCH 2/3] filling in syscall components --- crates/kernel/src/event/context.rs | 1 + crates/kernel/src/event/mod.rs | 63 +++++++++++++++++++++++++++++ crates/kernel/src/event/thread.rs | 15 ++++++- crates/kernel/src/process/mem.rs | 12 +++--- crates/kernel/src/process/signal.rs | 38 +++++++++++++++-- crates/kernel/src/syscall/mod.rs | 4 +- crates/kernel/src/syscall/signal.rs | 26 +++++++----- crates/ulib/src/sys.rs | 7 ++++ 8 files changed, 144 insertions(+), 22 deletions(-) diff --git a/crates/kernel/src/event/context.rs b/crates/kernel/src/event/context.rs index 22d89af9..f8df7947 100644 --- a/crates/kernel/src/event/context.rs +++ b/crates/kernel/src/event/context.rs @@ -74,6 +74,7 @@ unsafe impl Sync for AllCores {} /// The register context of a thread. #[repr(C)] +#[derive(Copy, Clone)] pub struct Context { pub regs: [usize; 31], pub kernel_sp: usize, diff --git a/crates/kernel/src/event/mod.rs b/crates/kernel/src/event/mod.rs index 1b5d5daa..41818de2 100644 --- a/crates/kernel/src/event/mod.rs +++ b/crates/kernel/src/event/mod.rs @@ -10,6 +10,9 @@ use alloc::boxed::Box; use context::{deschedule_thread, Context, DescheduleAction, CORES}; use scheduler::{Priority, Scheduler}; +use crate::process::signal::{SignalCode, SignalFlagOptions}; +use crate::syscall::proc::exit_user_thread; + pub static SCHEDULER: Scheduler = Scheduler::new(); pub struct Event { @@ -69,6 +72,66 @@ pub unsafe extern "C" fn run_event_loop() -> ! { func(); } EventKind::ScheduleThread(thread) => { + + if thread.is_user_thread() { + let proc = thread.process.as_ref().unwrap(); + + //Cleanup, if not in handler then invalidate backup + if !proc.signal_flags.contains(SignalFlagOptions::IN_HANDLER) && thread.backup_context.is_some() { + thread.backup_context = None; + } + + if proc.signal_flags.contains(SignalFlagOptions::IS_DEAD) { + exit_user_thread(thread, SignalCode::KilledUnblockable); + } else if proc.signal_flags.contains(SignalFlagOptions::IS_KILL) { + + if proc.signal_flags.contains(SignalFlagOptions::IN_HANDLER) { + //Received kill while in another signal handler + exit_user_thread(thread, SignalCode::InHandler); + } + + if let Some(kill_block_handler) = proc.signal_handlers.kill_block_handler { + //enter_thread will now use the backup context + proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true); + + thread.backup_context = Some(thread.context.unwrap()); + //replacing link register with the address of the handler and the + //the first two registers with the signal number and stack pointer + thread.backup_context.unwrap().regs[0] = SignalCode::KilledBlockable as usize; + thread.backup_context.unwrap().regs[30] = unsafe { core::mem::transmute::(kill_block_handler) }; + + //It is up to sigreturn to remove handler status which will then + //invalidate the secondary context + + } else { + //No kill block handler registered + exit_user_thread(thread, SignalCode::KilledBlockable); + } + } else if proc.signal_flags.contains(SignalFlagOptions::IS_PAGE_FAULT) { + //There should be a nicer way to write this with less code duplication + + if proc.signal_flags.contains(SignalFlagOptions::IN_HANDLER) { + //Received kill while in another signal handler + exit_user_thread(thread, SignalCode::InHandler); + } + + if let Some(page_fault_handler) = proc.signal_handlers.user_page_fault_handler { + //enter_thread will now use the backup context + proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true); + + thread.backup_context = Some(thread.context.unwrap()); + //replacing link register with the address of the handler and the + //the first two registers with the signal number and stack pointer + thread.backup_context.unwrap().regs[0] = SignalCode::PageFault as usize; + thread.backup_context.unwrap().regs[30] = unsafe { core::mem::transmute::(page_fault_handler) }; + + } else { + //No page fault handler registed + exit_user_thread(thread, SignalCode::PageFault); + } + } + } + unsafe { thread.enter_thread() }; } EventKind::AsyncTask(task_id) => { diff --git a/crates/kernel/src/event/thread.rs b/crates/kernel/src/event/thread.rs index a0591488..9d31e07d 100644 --- a/crates/kernel/src/event/thread.rs +++ b/crates/kernel/src/event/thread.rs @@ -12,12 +12,16 @@ use super::{Event, SCHEDULER}; /// while the thread isn't running, stores the saved register state of /// the thread. pub struct Thread { + //Pointer to the last context run on this thread. More documentation is offered below pub last_context: NonNull, pub stack: NonNull<[u128]>, // Stored on the thread's stack func: Option>, pub context: Option, + //Backup user context which stores the old context prior to the run of the handler + //This is necessary because we do not want to context to be modified inside of the handler + pub backup_context: Option, pub user_regs: Option, pub process: Option, pub priority: Priority, @@ -74,6 +78,7 @@ impl Thread { last_context: NonNull::dangling(), func: None, context: Some(data), + backup_context: None, user_regs: Some(UserRegs { ttbr0_el1: process.get_ttbr0(), usermode: true, @@ -118,6 +123,7 @@ impl Thread { last_context: context, func, context: None, + backup_context: None, user_regs: None, process: None, priority, @@ -176,7 +182,14 @@ impl Thread { unsafe { crate::sync::disable_interrupts() }; if let Some(user) = &self.user_regs { - let ctx = unsafe { &mut *next_ctx }; + //If in handler, use backup context as to not modify regular context + let proc = self.process.unwrap().as_ref(); + let ctx: &mut Context; + if proc.signal_flags.contains(crate::process::signal::SignalFlagOptions::IN_HANDLER) { + ctx = &mut self.backup_context.unwrap(); + } else { + ctx = unsafe { &mut *next_ctx }; + } unsafe { Self::restore_user_regs(user, ctx) }; } diff --git a/crates/kernel/src/process/mem.rs b/crates/kernel/src/process/mem.rs index 84d5d7fa..2c5d98b5 100644 --- a/crates/kernel/src/process/mem.rs +++ b/crates/kernel/src/process/mem.rs @@ -11,7 +11,7 @@ use crate::arch::memory::vmm::{ use crate::event::async_handler::{run_async_handler, HandlerContext}; use crate::event::context::Context; use crate::event::exceptions::DataAbortISS; -use crate::process::signal::SignalFlags; +use crate::process::signal::{SignalFlagOptions, SignalFlags}; use crate::syscall::proc::exit_user_thread; use super::fd::ArcFd; @@ -313,7 +313,7 @@ pub fn page_fault_handler(ctx: &mut Context, far: usize, _iss: DataAbortISS) -> run_async_handler(ctx, async move |mut context: HandlerContext<'_>| { let proc = context.cur_process().unwrap(); - if proc.signal_flags.contains(signal::SignalFlags::IN_HANDLER) { + if proc.signal_flags.contains(signal::SignalFlagOptions::IN_HANDLER) { println!("Page fault at addr {far:#10x} while in a signal handler"); } @@ -326,9 +326,11 @@ pub fn page_fault_handler(ctx: &mut Context, far: usize, _iss: DataAbortISS) -> None => { if let Some(user_page_fault_handler) = proc.signal_handlers.user_page_fault_handler { - proc.signal_flags.set(SignalFlags::IN_HANDLER, true); - //TODO: run page fault handler and plug in correct args - proc.signal_flags.set(SignalFlags::IN_HANDLER, false); + //The event loop handle the rest + proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true); + + //It is up to sigreturn to remove handler status and invalidate the secondary + //context } let exit_code = &proc.exit_code; diff --git a/crates/kernel/src/process/signal.rs b/crates/kernel/src/process/signal.rs index c95e477c..3ad51ace 100644 --- a/crates/kernel/src/process/signal.rs +++ b/crates/kernel/src/process/signal.rs @@ -1,4 +1,4 @@ - +use core::sync::atomic::{Ordering, AtomicU8}; #[derive(Debug)] pub struct SignalHandlers { @@ -6,14 +6,26 @@ pub struct SignalHandlers { pub kill_block_handler: Option, } +//These numbers are somewhat based on the Linux signal numbers +pub enum SignalCode { + InHandler = -1, + Interrupt = 2, + KilledUnblockable = 9, + PageFault = -4, //For compatibility with page fault handler + KilledBlockable = 15, +} + //TODO: replace this with a queue bitflags::bitflags! { #[derive(Clone, Copy, Debug)] - pub struct SignalFlags: u8 { + pub struct SignalFlagOptions: u8 { const IS_KILL = 1 << 0; //can block this const IS_DEAD = 1 << 1; //can't block this - const IN_HANDLER = 1 << 2; //currently not supporting nested signal handlers + const IS_PAGE_FAULT = 1 << 2; + //currently not supporting nested signal handlers + //WARN: this may break with multiple threads in a process + const IN_HANDLER = 1 << 3; } } @@ -24,3 +36,23 @@ impl SignalHandlers { } +pub struct SignalFlags(AtomicU8); + +impl SignalFlags { + pub fn empty() -> Self { + return SignalFlags(AtomicU8::new(0)); + } + + pub fn set(&self, option: SignalFlagOptions, val: bool) { + if val { + self.0.fetch_or(option as u8, Ordering::SeqCst); + } else { + self.0.fetch_and(!(option as u8), Ordering::SeqCst); + } + } + + pub fn contains(&self, flag: SignalFlagOptions) -> bool { + return self.0.load(Ordering::SeqCst) & (flag as u8); + } +} + diff --git a/crates/kernel/src/syscall/mod.rs b/crates/kernel/src/syscall/mod.rs index 10e8e2aa..5cf9c73c 100644 --- a/crates/kernel/src/syscall/mod.rs +++ b/crates/kernel/src/syscall/mod.rs @@ -45,8 +45,8 @@ pub unsafe fn register_syscalls() { register_syscall_handler(27, semaphore::sys_sem_up); register_syscall_handler(28, semaphore::sys_sem_down); - register_syscall_handler(33, signal::sys_register_user_page_fault_handler); - register_syscall_handler(34, signal::sys_register_kill_block_handler); + register_syscall_handler(33, signal::sys_register_signal_handler); + register_syscall_handler(34, signal::sys_sigreturn); register_syscall_handler(35, signal::sys_kill); register_syscall_handler(36, signal::sys_kill_unblockable); } diff --git a/crates/kernel/src/syscall/signal.rs b/crates/kernel/src/syscall/signal.rs index f04e14c5..c076a81e 100644 --- a/crates/kernel/src/syscall/signal.rs +++ b/crates/kernel/src/syscall/signal.rs @@ -1,34 +1,38 @@ use crate::event::async_handler::{run_async_handler, HandlerContext}; use crate::event::context::{deschedule_thread, Context, DescheduleAction, CORES}; -use crate::process::ExitStatus; +use crate::process::{signal, ExitStatus}; //TODO: register these syscalls //Users should be able to redefine these? ///Register a function to run when the user process encounters a page fault -pub unsafe fn sys_register_user_page_fault_handler(ctx: &mut Context) -> *mut Context { - let user_page_fault_handler: fn() = unsafe { core::mem::transmute::(ctx.regs[0]) }; +pub unsafe fn sys_register_signal_handler(ctx: &mut Context) -> *mut Context { + let signal_number= ctx.regs[0] as i32; + let user_handler: fn() = unsafe { core::mem::transmute::(ctx.regs[1]) }; //TODO: add safety checks on this pointer run_async_handler(ctx, async move |mut context: HandlerContext<'_>| { let proc = context.cur_process().unwrap(); - - proc.signal_handlers.user_page_fault_handler = Some(user_page_fault_handler); + + match signal_number { + signal::SignalCode::PageFault => proc.signal_handlers.user_page_fault_handler = Some(user_handler), + signal::SignalCode::KilledBlockable => proc.signal_handlers.kill_block_handler = Some(user_handler), + _ => panic!("Passed in unrecognized signal {}", signal_number); + } context.resume_return(0) }) } -///Register a function to run when the process receives a kill signal -pub unsafe fn sys_register_kill_block_handler(ctx: &mut Context) -> *mut Context { - let user_kill_block_handler: fn() = unsafe { core::mem::transmute::(ctx.regs[0]) }; - //TODO: add safety checks on this pointer +///Return from a signal handler +pub unsafe fn sys_sigreturn(ctx: &mut Context) -> *mut Context { run_async_handler(ctx, async move |mut context: HandlerContext<'_>| { let proc = context.cur_process().unwrap(); - - proc.signal_handlers.kill_block_handler = Some(user_kill_block_handler); + + proc.signal_flags.set(signal::SignalFlagOptions::IN_HANDLER, false); + //replacing context with backup context is done in event loop context.resume_return(0) }) diff --git a/crates/ulib/src/sys.rs b/crates/ulib/src/sys.rs index 05b302f5..c30fde7e 100644 --- a/crates/ulib/src/sys.rs +++ b/crates/ulib/src/sys.rs @@ -122,6 +122,13 @@ syscall!(26 => pub fn sys_sem_create(value: usize) -> isize); syscall!(27 => pub fn sys_sem_up(fd: usize) -> isize); syscall!(28 => pub fn sys_sem_down(fd: usize) -> isize); +syscall!(33 => pub fn sys_register_signal_handler(signal_number: usize, signal_handler: usize) -> isize); +//Shouldn't return +syscall!(34 => pub fn sys_sigreturn() -> isize); +syscall!(35 => pub fn sys_kill(pid: usize) -> isize); +syscall!(36 => pub fn sys_kill_unblockable(pid: usize) -> isize); + + /* * * * * * * * * * * * * * * * * * * */ /* Syscall wrappers */ /* * * * * * * * * * * * * * * * * * * */ From 2151bf36f76f5e562e84b113047a020d33746f29 Mon Sep 17 00:00:00 2001 From: S A Date: Fri, 2 May 2025 14:49:32 -0500 Subject: [PATCH 3/3] futher work on signals --- crates/init/build.sh | 3 ++ crates/init/signals_test.rs | 81 +++++++++++++++++++++++++++++ crates/kernel/src/event/mod.rs | 19 +++---- crates/kernel/src/event/thread.rs | 12 ++--- crates/kernel/src/process.rs | 6 ++- crates/kernel/src/process/mem.rs | 25 +++++---- crates/kernel/src/process/signal.rs | 41 ++++++++++++--- crates/kernel/src/syscall/signal.rs | 17 +++--- crates/ulib/src/sys.rs | 37 +++++++++++++ 9 files changed, 197 insertions(+), 44 deletions(-) create mode 100755 crates/init/signals_test.rs diff --git a/crates/init/build.sh b/crates/init/build.sh index 352645fc..2c1498dc 100755 --- a/crates/init/build.sh +++ b/crates/init/build.sh @@ -13,6 +13,9 @@ mkdir -p fs # ./ls.rs # cp ls.elf fs/ +./signals_test.rs +cp signals_test.elf fs/ + cargo run -q -p initfs --bin util \ -- create --compress --out fs.arc --root fs fs --verbose diff --git a/crates/init/signals_test.rs b/crates/init/signals_test.rs new file mode 100755 index 00000000..b7c90017 --- /dev/null +++ b/crates/init/signals_test.rs @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +#![doc = r##""##] +#![no_std] +#![no_main] + +#[macro_use] +extern crate ulib; + +use ulib::sys::{exit, register_signal_handler, mmap, sys_sigreturn, sys_kill, sys_kill_unblockable, SignalCode}; + +fn page_fault_handler(_signal_number: u32, fault_addr: *mut ()) { + + println!("Inside of the user page fault handler, page fault at address {:x}", fault_addr as usize); + let _mmap_addr: *mut u8 = + unsafe { mmap(0, 4096, 0, 1 << 1, 0, 0).unwrap() } as *mut u8; + println!("Memory range is mmaped!"); + + unsafe { sys_sigreturn(); } + unreachable!(); +} + +#[no_mangle] +fn main() { + + let _root_fd = 3; + let page_fault_handler_ptr: fn() = unsafe { core::mem::transmute(page_fault_handler) }; + register_signal_handler(SignalCode::PageFault, page_fault_handler_ptr); + + static HELLO_CHARS: [u8; 5] = *b"hello"; + const VIRTUAL_ADDR: usize = 0x1E00000; + let hello_addr = VIRTUAL_ADDR as *mut u8; + + //Writing to unmappred region, user page fault handler should trigger + unsafe { + core::ptr::copy_nonoverlapping( + &raw const HELLO_CHARS[0], + hello_addr, + HELLO_CHARS.len(), + ); + } + + for i in 0..5 { + let curr_char: u8 = unsafe { *(hello_addr.wrapping_add(i)) }; + if curr_char != HELLO_CHARS[i] { + panic!( + "incorrect character at index {}! Expected {} got {}", + i, HELLO_CHARS[i] as char, curr_char as char + ); + } + } + + println!("signal handler test succeeded!"); + + //TODO: add kill and kill unblockable tests + + exit(15); +} diff --git a/crates/kernel/src/event/mod.rs b/crates/kernel/src/event/mod.rs index 41818de2..c2256ad6 100644 --- a/crates/kernel/src/event/mod.rs +++ b/crates/kernel/src/event/mod.rs @@ -71,7 +71,7 @@ pub unsafe extern "C" fn run_event_loop() -> ! { EventKind::Function(func) => { func(); } - EventKind::ScheduleThread(thread) => { + EventKind::ScheduleThread(mut thread) => { if thread.is_user_thread() { let proc = thread.process.as_ref().unwrap(); @@ -82,22 +82,22 @@ pub unsafe extern "C" fn run_event_loop() -> ! { } if proc.signal_flags.contains(SignalFlagOptions::IS_DEAD) { - exit_user_thread(thread, SignalCode::KilledUnblockable); + unsafe { exit_user_thread(thread, SignalCode::KillUnblockable.into()) }; } else if proc.signal_flags.contains(SignalFlagOptions::IS_KILL) { if proc.signal_flags.contains(SignalFlagOptions::IN_HANDLER) { //Received kill while in another signal handler - exit_user_thread(thread, SignalCode::InHandler); + unsafe { exit_user_thread(thread, SignalCode::InHandler.into()) }; } - if let Some(kill_block_handler) = proc.signal_handlers.kill_block_handler { + if let Some(kill_block_handler) = proc.signal_handlers.lock().kill_block_handler { //enter_thread will now use the backup context proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true); thread.backup_context = Some(thread.context.unwrap()); //replacing link register with the address of the handler and the //the first two registers with the signal number and stack pointer - thread.backup_context.unwrap().regs[0] = SignalCode::KilledBlockable as usize; + thread.backup_context.unwrap().regs[0] = SignalCode::KillBlockable as usize; thread.backup_context.unwrap().regs[30] = unsafe { core::mem::transmute::(kill_block_handler) }; //It is up to sigreturn to remove handler status which will then @@ -105,17 +105,17 @@ pub unsafe extern "C" fn run_event_loop() -> ! { } else { //No kill block handler registered - exit_user_thread(thread, SignalCode::KilledBlockable); + unsafe { exit_user_thread(thread, SignalCode::KillBlockable.into()) }; } } else if proc.signal_flags.contains(SignalFlagOptions::IS_PAGE_FAULT) { //There should be a nicer way to write this with less code duplication if proc.signal_flags.contains(SignalFlagOptions::IN_HANDLER) { //Received kill while in another signal handler - exit_user_thread(thread, SignalCode::InHandler); + unsafe { exit_user_thread(thread, SignalCode::InHandler.into()) }; } - if let Some(page_fault_handler) = proc.signal_handlers.user_page_fault_handler { + if let Some(page_fault_handler) = proc.signal_handlers.lock().user_page_fault_handler { //enter_thread will now use the backup context proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true); @@ -123,11 +123,12 @@ pub unsafe extern "C" fn run_event_loop() -> ! { //replacing link register with the address of the handler and the //the first two registers with the signal number and stack pointer thread.backup_context.unwrap().regs[0] = SignalCode::PageFault as usize; + //TODO: save the fault address into register 1 thread.backup_context.unwrap().regs[30] = unsafe { core::mem::transmute::(page_fault_handler) }; } else { //No page fault handler registed - exit_user_thread(thread, SignalCode::PageFault); + unsafe { exit_user_thread(thread, SignalCode::PageFault.into()) }; } } } diff --git a/crates/kernel/src/event/thread.rs b/crates/kernel/src/event/thread.rs index 9d31e07d..564a2961 100644 --- a/crates/kernel/src/event/thread.rs +++ b/crates/kernel/src/event/thread.rs @@ -183,13 +183,13 @@ impl Thread { if let Some(user) = &self.user_regs { //If in handler, use backup context as to not modify regular context - let proc = self.process.unwrap().as_ref(); - let ctx: &mut Context; - if proc.signal_flags.contains(crate::process::signal::SignalFlagOptions::IN_HANDLER) { - ctx = &mut self.backup_context.unwrap(); + let proc = self.process.as_ref().unwrap(); + let ctx: &mut Context = if proc.signal_flags.contains(crate::process::signal::SignalFlagOptions::IN_HANDLER) { + &mut self.backup_context.unwrap() } else { - ctx = unsafe { &mut *next_ctx }; - } + unsafe { &mut *next_ctx } + }; + unsafe { Self::restore_user_regs(user, ctx) }; } diff --git a/crates/kernel/src/process.rs b/crates/kernel/src/process.rs index 1904561a..005b8a12 100644 --- a/crates/kernel/src/process.rs +++ b/crates/kernel/src/process.rs @@ -23,7 +23,7 @@ pub struct Process { pub root: Option, pub file_descriptors: SpinLock, pub exit_code: Arc>, - pub signal_handlers: signal::SignalHandlers, + pub signal_handlers: SpinLock, pub signal_flags: signal::SignalFlags, // TODO: replace this with signal queue } @@ -36,7 +36,7 @@ impl Process { root: None, file_descriptors: SpinLock::new(FileDescriptorList { desc: Vec::new() }), exit_code: Arc::new(BlockingOnceCell::new()), - signal_handlers: signal::SignalHandlers::new(), + signal_handlers: SpinLock::new(signal::SignalHandlers::new()), signal_flags: signal::SignalFlags::empty(), } } @@ -66,6 +66,8 @@ impl Process { root: self.root.clone(), file_descriptors: SpinLock::new(new_fds), exit_code: Arc::new(BlockingOnceCell::new()), + signal_handlers: SpinLock::new(signal::SignalHandlers::new()), + signal_flags: signal::SignalFlags::empty(), }; new_process diff --git a/crates/kernel/src/process/mem.rs b/crates/kernel/src/process/mem.rs index 2c5d98b5..a44c6b7b 100644 --- a/crates/kernel/src/process/mem.rs +++ b/crates/kernel/src/process/mem.rs @@ -325,25 +325,28 @@ pub fn page_fault_handler(ctx: &mut Context, far: usize, _iss: DataAbortISS) -> match vme { None => { - if let Some(user_page_fault_handler) = proc.signal_handlers.user_page_fault_handler { + if let Some(_user_page_fault_handler) = proc.signal_handlers.lock().user_page_fault_handler { //The event loop handle the rest proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true); //It is up to sigreturn to remove handler status and invalidate the secondary //context - } + drop(mem); + context.resume_final() + } else { - let exit_code = &proc.exit_code; - exit_code.set(crate::process::ExitStatus { - status: -1i32 as u32, - }); - drop(mem); + let exit_code = &proc.exit_code; + exit_code.set(crate::process::ExitStatus { + status: signal::SignalCode::PageFault.into(), + }); + drop(mem); - println!("Invalid user access at addr {far:#10x}"); - println!("{:#?}", &*context.regs()); + println!("Invalid user access at addr {far:#10x}"); + println!("{:#?}", &*context.regs()); - let thread = context.detach_thread(); - unsafe { exit_user_thread(thread, -4i32 as u32) } + let thread = context.detach_thread(); + unsafe { exit_user_thread(thread, -4i32 as u32) } + } } Some(vme) => { mem.populate_page(vme, page_addr).await.unwrap(); // TODO: errors? diff --git a/crates/kernel/src/process/signal.rs b/crates/kernel/src/process/signal.rs index 3ad51ace..6ba64717 100644 --- a/crates/kernel/src/process/signal.rs +++ b/crates/kernel/src/process/signal.rs @@ -8,11 +8,36 @@ pub struct SignalHandlers { //These numbers are somewhat based on the Linux signal numbers pub enum SignalCode { - InHandler = -1, + InHandler = u32::MAX as isize, Interrupt = 2, - KilledUnblockable = 9, - PageFault = -4, //For compatibility with page fault handler - KilledBlockable = 15, + KillUnblockable = 9, + PageFault = 11, + KillBlockable = 15, +} + +impl From for SignalCode { + fn from(value: u32) -> SignalCode { + match value { + u32::MAX => SignalCode::InHandler, + 2 => SignalCode::Interrupt, + 9 => SignalCode::KillUnblockable, + 11 => SignalCode::PageFault, + 15 => SignalCode::KillBlockable, + _ => panic!("Failed to convert u32 {} into signal code", value), + } + } +} + +impl From for u32 { + fn from(value: SignalCode) -> u32 { + match value { + SignalCode::InHandler => u32::MAX, + SignalCode::Interrupt => 2, + SignalCode::KillUnblockable => 9, + SignalCode::PageFault => 11, + SignalCode::KillBlockable => 15, + } + } } //TODO: replace this with a queue @@ -43,16 +68,16 @@ impl SignalFlags { return SignalFlags(AtomicU8::new(0)); } - pub fn set(&self, option: SignalFlagOptions, val: bool) { + pub fn set(&self, flag: SignalFlagOptions, val: bool) { if val { - self.0.fetch_or(option as u8, Ordering::SeqCst); + self.0.fetch_or(flag.bits(), Ordering::SeqCst); } else { - self.0.fetch_and(!(option as u8), Ordering::SeqCst); + self.0.fetch_and(!flag.bits(), Ordering::SeqCst); } } pub fn contains(&self, flag: SignalFlagOptions) -> bool { - return self.0.load(Ordering::SeqCst) & (flag as u8); + return (self.0.load(Ordering::SeqCst) & flag.bits()) != 0; } } diff --git a/crates/kernel/src/syscall/signal.rs b/crates/kernel/src/syscall/signal.rs index c076a81e..b60650b9 100644 --- a/crates/kernel/src/syscall/signal.rs +++ b/crates/kernel/src/syscall/signal.rs @@ -1,5 +1,6 @@ use crate::event::async_handler::{run_async_handler, HandlerContext}; use crate::event::context::{deschedule_thread, Context, DescheduleAction, CORES}; +use crate::process::signal::SignalCode; use crate::process::{signal, ExitStatus}; //TODO: register these syscalls @@ -8,20 +9,20 @@ use crate::process::{signal, ExitStatus}; ///Register a function to run when the user process encounters a page fault pub unsafe fn sys_register_signal_handler(ctx: &mut Context) -> *mut Context { - let signal_number= ctx.regs[0] as i32; + let signal_number= ctx.regs[0] as u32; let user_handler: fn() = unsafe { core::mem::transmute::(ctx.regs[1]) }; //TODO: add safety checks on this pointer - + let mut ret_val = 0; run_async_handler(ctx, async move |mut context: HandlerContext<'_>| { let proc = context.cur_process().unwrap(); - - match signal_number { - signal::SignalCode::PageFault => proc.signal_handlers.user_page_fault_handler = Some(user_handler), - signal::SignalCode::KilledBlockable => proc.signal_handlers.kill_block_handler = Some(user_handler), - _ => panic!("Passed in unrecognized signal {}", signal_number); + let signal_code = SignalCode::from(signal_number); + match signal_code { + signal::SignalCode::PageFault => proc.signal_handlers.lock().user_page_fault_handler = Some(user_handler), + signal::SignalCode::KillBlockable => proc.signal_handlers.lock().kill_block_handler = Some(user_handler), + _ => ret_val = -1, //User is trying to register a handler for an unsupported signal } - context.resume_return(0) + context.resume_return(ret_val as usize) }) } diff --git a/crates/ulib/src/sys.rs b/crates/ulib/src/sys.rs index c30fde7e..b29fb4ea 100644 --- a/crates/ulib/src/sys.rs +++ b/crates/ulib/src/sys.rs @@ -348,3 +348,40 @@ extern "C" fn exec_child(spawn_args: *const SpawnArgs) -> ! { // TODO: notify parent of spawn failure exit(1); } + +pub enum SignalCode { + Interrupt = 2, + KillUnblockable = 9, + PageFault = 11, + KillBlockable = 15, +} + +impl From for SignalCode { + fn from(value: u32) -> SignalCode { + match value { + 2 => SignalCode::Interrupt, + 9 => SignalCode::KillUnblockable, + 11 => SignalCode::PageFault, + 15 => SignalCode::KillBlockable, + _ => panic!("Failed to convert u32 {} into signal code", value), + } + } +} + +impl From for u32 { + fn from(value: SignalCode) -> u32 { + match value { + SignalCode::Interrupt => 2, + SignalCode::KillUnblockable => 9, + SignalCode::PageFault => 11, + SignalCode::KillBlockable => 15, + } + } +} + +//Currently the kernel only supports registering handlers for PageFault and KillBlockable +pub unsafe fn register_signal_handler(code: SignalCode, handler: fn()) -> Result { + let code_as_int: u32 = code.into(); + let res = unsafe { sys_register_signal_handler(code_as_int as usize, core::mem::transmute::(handler)) }; + int_to_error(res) +}