feature: basic signal handling
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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),
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user