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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user