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!()
}
}
}