feature: basic signal handling

This commit is contained in:
2021-11-11 13:46:36 +02:00
parent dacbea02d6
commit afc6ccc8dd
11 changed files with 261 additions and 18 deletions
+33 -4
View File
@@ -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);
+3 -3
View File
@@ -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 {
+5 -1
View File
@@ -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
}
}
+111 -5
View File
@@ -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<ExitCode>,
signal_entry: usize,
signal_stack: usize,
}
/// Structure describing an operating system process
#[allow(dead_code)]
pub struct Process {
ctx: UnsafeCell<Context>,
signal_ctx: UnsafeCell<Context>,
inner: IrqSafeSpinLock<ProcessInner>,
exit_wait: Wait,
signal_state: AtomicU32,
signal_pending: AtomicU32,
/// Process I/O context
pub io: IrqSafeSpinLock<ProcessIo>,
}
@@ -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),
+34 -5
View File
@@ -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<usize, Errno> {
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<usize, Errno> {
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<usize, Errno> {
}
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!()
}
}
}
+14
View File
@@ -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());
}
}
+4
View File
@@ -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;
+17
View File
@@ -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
}
+1
View File
@@ -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;
+29
View File
@@ -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<u32> for Signal {
type Error = Errno;
#[inline]
fn try_from(u: u32) -> Result<Self, Errno> {
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)
}
}
}
+10
View File
@@ -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;
}