From afc6ccc8dd8030d22388fea9f2c89dfd3f80b0ff Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Thu, 11 Nov 2021 13:46:36 +0200 Subject: [PATCH] feature: basic signal handling --- kernel/src/arch/aarch64/context.rs | 37 ++++++++- kernel/src/arch/aarch64/exception.rs | 6 +- kernel/src/mem/virt/table.rs | 6 +- kernel/src/proc/process.rs | 116 +++++++++++++++++++++++++-- kernel/src/syscall/mod.rs | 39 +++++++-- libusr/src/lib.rs | 14 ++++ syscall/src/abi.rs | 4 + syscall/src/calls.rs | 17 ++++ syscall/src/lib.rs | 1 + syscall/src/signal.rs | 29 +++++++ user/src/shell/main.rs | 10 +++ 11 files changed, 261 insertions(+), 18 deletions(-) create mode 100644 syscall/src/signal.rs diff --git a/kernel/src/arch/aarch64/context.rs b/kernel/src/arch/aarch64/context.rs index b86f0bd..aa48ac8 100644 --- a/kernel/src/arch/aarch64/context.rs +++ b/kernel/src/arch/aarch64/context.rs @@ -18,7 +18,7 @@ pub struct Context { /// Thread's kernel stack pointer pub k_sp: usize, // 0x00 - stack_base_phys: usize, + stack_base: usize, stack_page_count: usize, } @@ -35,7 +35,7 @@ impl Context { Self { k_sp: stack.sp, - stack_base_phys: stack.bp, + stack_base: stack.bp, stack_page_count: 8, } } @@ -85,7 +85,7 @@ impl Context { Self { k_sp: stack.sp, - stack_base_phys: stack.bp, + stack_base: stack.bp, stack_page_count: 8, } } @@ -104,11 +104,33 @@ impl Context { Self { k_sp: stack.sp, - stack_base_phys: stack.bp, + stack_base: stack.bp, stack_page_count: 8, } } + pub fn user_empty() -> Self { + let mut stack = Stack::new(8); + Self { + k_sp: stack.sp, + stack_base: stack.bp, + stack_page_count: 8 + } + } + + pub unsafe fn setup_signal_entry(&mut self, entry: usize, arg: usize, ttbr0: usize, ustack: usize) { + let mut stack = Stack::from_base_size(self.stack_base, self.stack_page_count); + + stack.push(entry); + stack.push(arg); + stack.push(0); + stack.push(ustack); + + stack.setup_common(__aa64_ctx_enter_user as usize, ttbr0); + + self.k_sp = stack.sp; + } + /// Performs initial thread entry /// /// # Safety @@ -140,6 +162,13 @@ impl Stack { } } + pub unsafe fn from_base_size(bp: usize, page_count: usize) -> Stack { + Stack { + bp, + sp: bp + page_count * mem::PAGE_SIZE + } + } + pub fn setup_common(&mut self, entry: usize, ttbr: usize) { self.push(0); self.push(ttbr); diff --git a/kernel/src/arch/aarch64/exception.rs b/kernel/src/arch/aarch64/exception.rs index 7be2b26..e1e786d 100644 --- a/kernel/src/arch/aarch64/exception.rs +++ b/kernel/src/arch/aarch64/exception.rs @@ -6,7 +6,7 @@ use crate::dev::irq::{IntController, IrqContext}; use crate::proc::{sched, Process}; use crate::mem; use crate::syscall; -use ::syscall::abi; +use ::syscall::{abi, signal::Signal}; use cortex_a::registers::{ESR_EL1, FAR_EL1}; use tock_registers::interfaces::Readable; @@ -76,7 +76,7 @@ fn dump_data_abort(level: Level, esr: u64, far: u64) { } else { print!(level, " at UNKNOWN"); } - println!(level, ""); + println!(level, "\x1B[0m"); } #[no_mangle] @@ -95,7 +95,7 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) { if let Err(e) = proc.manipulate_space(|space| space.try_cow_copy(far)) { // Kill program dump_data_abort(Level::Error, esr, far as u64); - panic!("CoW copy returned {:?}", e); + proc.enter_signal(Signal::SegmentationFault); } unsafe { diff --git a/kernel/src/mem/virt/table.rs b/kernel/src/mem/virt/table.rs index 3f099b8..bd74a9c 100644 --- a/kernel/src/mem/virt/table.rs +++ b/kernel/src/mem/virt/table.rs @@ -204,7 +204,7 @@ impl Space { } else { l2_table[l2i] = Entry::table(phys, flags | MapAttributes::ACCESS); #[cfg(feature = "verbose")] - debugln!("Map {:#x} -> {:#x}", virt, phys); + debugln!("{:#p} Map {:#x} -> {:#x}, {:?}", self, virt, phys, flags); Ok(()) } } @@ -323,4 +323,8 @@ impl Space { mem::memset(space as *mut Space as *mut u8, 0, 4096); } } + + pub fn address_phys(&mut self) -> usize { + (self as *mut _ as usize) - mem::KERNEL_OFFSET + } } diff --git a/kernel/src/proc/process.rs b/kernel/src/proc/process.rs index 9d2c2c1..dac3453 100644 --- a/kernel/src/proc/process.rs +++ b/kernel/src/proc/process.rs @@ -12,6 +12,7 @@ use core::cell::UnsafeCell; use core::fmt; use core::sync::atomic::{AtomicU32, Ordering}; use error::Errno; +use syscall::signal::Signal; pub use crate::arch::platform::context::{self, Context}; @@ -47,14 +48,19 @@ struct ProcessInner { id: Pid, wait_flag: bool, exit: Option, + signal_entry: usize, + signal_stack: usize, } /// Structure describing an operating system process #[allow(dead_code)] pub struct Process { ctx: UnsafeCell, + signal_ctx: UnsafeCell, inner: IrqSafeSpinLock, exit_wait: Wait, + signal_state: AtomicU32, + signal_pending: AtomicU32, /// Process I/O context pub io: IrqSafeSpinLock, } @@ -154,6 +160,90 @@ impl Process { PROCESSES.lock().get(&pid).cloned() } + pub fn set_signal(&self, signal: Signal) { + let mut lock = self.inner.lock(); + + match lock.state { + State::Running => { + drop(lock); + self.enter_signal(signal); + } + State::Waiting => { + // TODO abort whatever the process is waiting for + todo!() + } + State::Ready => { + todo!() + } + State::Finished => { + // TODO report error back + todo!() + } + } + } + + pub fn return_from_signal(&self) { + if self.signal_pending.load(Ordering::Acquire) == 0 { + panic!("TODO handle cases when returning from no signal"); + } + self.signal_pending.store(0, Ordering::Release); + + let src_ctx = self.signal_ctx.get(); + let dst_ctx = self.ctx.get(); + + assert_eq!(self.inner.lock().state, State::Running); + + unsafe { + (&mut *src_ctx).switch(&mut *dst_ctx); + } + } + + pub fn enter_signal(&self, signal: Signal) { + if self + .signal_pending + .compare_exchange_weak(0, signal as u32, Ordering::SeqCst, Ordering::Relaxed) + .is_err() + { + panic!("Already handling a signal (maybe handle this case)"); + } + + let mut lock = self.inner.lock(); + let signal_ctx = unsafe { &mut *self.signal_ctx.get() }; + + let dst_id = lock.id; + let dst_space_phys = lock.space.as_mut().unwrap().address_phys(); + let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48); + + debugln!( + "Signal entry: pc={:#x}, sp={:#x}, ttbr0={:#x}", + lock.signal_entry, + lock.signal_stack, + dst_ttbr0 + ); + assert_eq!(lock.state, State::Running); + + unsafe { + signal_ctx.setup_signal_entry( + lock.signal_entry, + signal as usize, + dst_ttbr0, + lock.signal_stack, + ); + } + let src_ctx = self.ctx.get(); + drop(lock); + + unsafe { + (&mut *src_ctx).switch(signal_ctx); + } + } + + pub fn setup_signal_context(&self, entry: usize, stack: usize) { + let mut lock = self.inner.lock(); + lock.signal_entry = entry; + lock.signal_stack = stack; + } + /// Schedules an initial thread for execution /// /// # Safety @@ -163,9 +253,7 @@ impl Process { pub unsafe fn enter(proc: ProcessRef) -> ! { // FIXME use some global lock to guarantee atomicity of thread entry? proc.inner.lock().state = State::Running; - let ctx = proc.ctx.get(); - - (&mut *ctx).enter() + proc.current_context().enter() } #[inline] @@ -176,6 +264,14 @@ impl Process { f(self.inner.lock().space.as_mut().unwrap()) } + fn current_context(&self) -> &mut Context { + if self.signal_pending.load(Ordering::Acquire) != 0 { + unsafe { &mut *self.signal_ctx.get() } + } else { + unsafe { &mut *self.ctx.get() } + } + } + /// Schedules a next thread for execution /// /// # Safety @@ -197,8 +293,8 @@ impl Process { dst_lock.state = State::Running; } - let src_ctx = src.ctx.get(); - let dst_ctx = dst.ctx.get(); + let src_ctx = src.current_context(); + let dst_ctx = dst.current_context(); (&mut *src_ctx).switch(&mut *dst_ctx); } @@ -237,9 +333,14 @@ impl Process { let id = Pid::new_kernel(); let res = Rc::new(Self { ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)), + signal_ctx: UnsafeCell::new(Context::user_empty()), io: IrqSafeSpinLock::new(ProcessIo::new()), exit_wait: Wait::new(), + signal_state: AtomicU32::new(0), + signal_pending: AtomicU32::new(0), inner: IrqSafeSpinLock::new(ProcessInner { + signal_entry: 0, + signal_stack: 0, id, exit: None, space: None, @@ -265,9 +366,14 @@ impl Process { let dst = Rc::new(Self { ctx: UnsafeCell::new(Context::fork(frame, dst_ttbr0)), + signal_ctx: UnsafeCell::new(Context::user_empty()), io: IrqSafeSpinLock::new(src_io.fork()?), exit_wait: Wait::new(), + signal_state: AtomicU32::new(0), + signal_pending: AtomicU32::new(0), inner: IrqSafeSpinLock::new(ProcessInner { + signal_entry: 0, + signal_stack: 0, id: dst_id, exit: None, space: Some(dst_space), diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index aa3d68a..bbcb11e 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -10,9 +10,10 @@ use error::Errno; use libcommon::{Read, Write}; use syscall::{ abi, + signal::Signal, stat::{AT_EMPTY_PATH, AT_FDCWD}, }; -use vfs::{FileMode, OpenFlags, Stat, VnodeRef, IoctlCmd}; +use vfs::{FileMode, IoctlCmd, OpenFlags, Stat, VnodeRef}; pub mod arg; pub use arg::*; @@ -131,8 +132,8 @@ pub fn syscall(num: usize, args: &[usize]) -> Result { Ok(exit) => { *status = i32::from(exit); Ok(0) - }, - _ => todo!() + } + _ => todo!(), } } abi::SYS_IOCTL => { @@ -144,7 +145,7 @@ pub fn syscall(num: usize, args: &[usize]) -> Result { let node = io.file(fd)?.borrow().node().ok_or(Errno::InvalidFile)?; node.ioctl(cmd, args[2], args[3]) - }, + } // Extra system calls abi::SYS_EX_DEBUG_TRACE => { @@ -168,6 +169,34 @@ pub fn syscall(num: usize, args: &[usize]) -> Result { } res.map(|_| 0) } - _ => panic!("Undefined system call: {}", num), + abi::SYS_EX_SIGNAL => { + let proc = Process::current(); + proc.setup_signal_context(args[0], args[1]); + Ok(0) + } + abi::SYS_EX_SIGRETURN => { + let proc = Process::current(); + proc.return_from_signal(); + panic!("This code won't run"); + } + abi::SYS_EX_KILL => { + let pid = args[0] as i32; + let signal = Signal::try_from(args[1] as u32)?; + let proc = if pid > 0 { + Process::get(unsafe { Pid::from_raw(pid as u32) }).ok_or(Errno::DoesNotExist)? + } else if pid == 0 { + Process::current() + } else { + todo!() + }; + proc.set_signal(signal); + Ok(0) + } + _ => { + let proc = Process::current(); + errorln!("Undefined system call: {}", num); + proc.enter_signal(Signal::InvalidSystemCall); + todo!() + } } } diff --git a/libusr/src/lib.rs b/libusr/src/lib.rs index ee8532b..c17b619 100644 --- a/libusr/src/lib.rs +++ b/libusr/src/lib.rs @@ -10,9 +10,20 @@ pub mod os; pub mod sys { pub use syscall::calls::*; pub use syscall::stat::*; + pub use syscall::signal::Signal; pub use syscall::termios; } +#[inline(never)] +extern "C" fn _signal_handler(arg: sys::Signal) -> ! { + trace!("Entered signal handler: arg={:?}", arg); + unsafe { + sys::sys_ex_sigreturn(); + } +} + +static mut SIGNAL_STACK: [u8; 4096] = [0; 4096]; + #[link_section = ".text._start"] #[no_mangle] extern "C" fn _start(_arg: usize) -> ! { @@ -20,6 +31,9 @@ extern "C" fn _start(_arg: usize) -> ! { fn main() -> i32; } unsafe { + SIGNAL_STACK[0] = 1; + sys::sys_ex_signal(_signal_handler as usize, SIGNAL_STACK.as_ptr() as usize + 4096); + sys::sys_exit(main()); } } diff --git a/syscall/src/abi.rs b/syscall/src/abi.rs index 3cff16c..b14c321 100644 --- a/syscall/src/abi.rs +++ b/syscall/src/abi.rs @@ -1,6 +1,10 @@ pub const SYS_EX_DEBUG_TRACE: usize = 128; pub const SYS_EX_NANOSLEEP: usize = 129; +pub const SYS_EX_SIGNAL: usize = 130; +pub const SYS_EX_SIGRETURN: usize = 131; +pub const SYS_EX_KILL: usize = 132; + pub const SYS_EXIT: usize = 1; pub const SYS_READ: usize = 2; pub const SYS_WRITE: usize = 3; diff --git a/syscall/src/calls.rs b/syscall/src/calls.rs index 8e347c6..fa14257 100644 --- a/syscall/src/calls.rs +++ b/syscall/src/calls.rs @@ -1,6 +1,7 @@ use crate::abi; use crate::{ ioctl::IoctlCmd, + signal::Signal, stat::{FileMode, OpenFlags, Stat}, }; use core::mem::size_of; @@ -198,3 +199,19 @@ pub unsafe fn sys_ioctl(fd: u32, cmd: IoctlCmd, ptr: usize, len: usize) -> isize argn!(len) ) as isize } + +#[inline(always)] +pub unsafe fn sys_ex_signal(entry: usize, stack: usize) { + syscall!(abi::SYS_EX_SIGNAL, argn!(entry), argn!(stack)); +} + +#[inline(always)] +pub unsafe fn sys_ex_sigreturn() -> ! { + syscall!(abi::SYS_EX_SIGRETURN); + unreachable!(); +} + +#[inline(always)] +pub unsafe fn sys_ex_kill(pid: u32, signum: Signal) -> i32 { + syscall!(abi::SYS_EX_KILL, argn!(pid), argn!(signum as u32)) as i32 +} diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index 1b8eb00..e214f37 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -8,6 +8,7 @@ pub mod abi; pub mod stat; pub mod ioctl; pub mod termios; +pub mod signal; #[cfg(feature = "user")] pub mod calls; diff --git a/syscall/src/signal.rs b/syscall/src/signal.rs new file mode 100644 index 0000000..eeb47ec --- /dev/null +++ b/syscall/src/signal.rs @@ -0,0 +1,29 @@ +use error::Errno; + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(u32)] +pub enum Signal { + Interrupt = 2, + IllegalInstruction = 4, + FloatError = 8, + Kill = 9, + SegmentationFault = 11, + InvalidSystemCall = 31 +} + +impl TryFrom for Signal { + type Error = Errno; + + #[inline] + fn try_from(u: u32) -> Result { + match u { + 2 => Ok(Self::Interrupt), + 4 => Ok(Self::IllegalInstruction), + 8 => Ok(Self::FloatError), + 9 => Ok(Self::Kill), + 11 => Ok(Self::SegmentationFault), + 31 => Ok(Self::InvalidSystemCall), + _ => Err(Errno::InvalidArgument) + } + } +} diff --git a/user/src/shell/main.rs b/user/src/shell/main.rs index b327102..48157aa 100644 --- a/user/src/shell/main.rs +++ b/user/src/shell/main.rs @@ -4,6 +4,8 @@ #[macro_use] extern crate libusr; +use libusr::sys::Signal; + fn readline(fd: i32, buf: &mut [u8]) -> Result<&str, ()> { let count = unsafe { libusr::sys::sys_read(fd, buf) }; if count >= 0 { @@ -26,6 +28,14 @@ fn main() -> i32 { println!(":: {:?}", line); + if line == "test" { + unsafe { + libusr::sys::sys_ex_kill(0, Signal::Interrupt); + } + trace!("Returned from signal"); + continue; + } + if line == "quit" || line == "exit" { break; }