Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/init/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ mkdir -p fs
# ./ls.rs
# cp ls.elf fs/

./signals_test.rs
cp signals_test.elf fs/

cargo run -q -p initfs --bin util \
-- create --compress --out fs.arc --root fs fs --verbose

Expand Down
81 changes: 81 additions & 0 deletions crates/init/signals_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
#![doc = r##"<!-- Absolutely cursed hacks:
NAME="$(basename "$0" .rs)"
DIR=$(cd "$(dirname "$0")" && pwd -P)
ULIB_DIR="$(git rev-parse --show-toplevel)/crates/ulib"
exec "$ULIB_DIR/compile.sh" "$0" <<END_MANIFEST
[package]
name = "$NAME"
version = "0.1.0"
edition = "2021"

[[bin]]
name = "$NAME"
path = "$DIR/$NAME.rs"

[dependencies]
ulib = { path = "$ULIB_DIR" }

[profile.standalone]
inherits = "release"
opt-level = 0
panic = "abort"
strip = "debuginfo"

END_MANIFEST
exit # -->"##]
#![no_std]
#![no_main]

#[macro_use]
extern crate ulib;

use ulib::sys::{exit, register_signal_handler, mmap, sys_sigreturn, sys_kill, sys_kill_unblockable, SignalCode};

fn page_fault_handler(_signal_number: u32, fault_addr: *mut ()) {

println!("Inside of the user page fault handler, page fault at address {:x}", fault_addr as usize);
let _mmap_addr: *mut u8 =
unsafe { mmap(0, 4096, 0, 1 << 1, 0, 0).unwrap() } as *mut u8;
println!("Memory range is mmaped!");

unsafe { sys_sigreturn(); }
unreachable!();
}

#[no_mangle]
fn main() {

let _root_fd = 3;
let page_fault_handler_ptr: fn() = unsafe { core::mem::transmute<fn(u32, *mut ()), fn()>(page_fault_handler) };
register_signal_handler(SignalCode::PageFault, page_fault_handler_ptr);

static HELLO_CHARS: [u8; 5] = *b"hello";
const VIRTUAL_ADDR: usize = 0x1E00000;
let hello_addr = VIRTUAL_ADDR as *mut u8;

//Writing to unmappred region, user page fault handler should trigger
unsafe {
core::ptr::copy_nonoverlapping(
&raw const HELLO_CHARS[0],
hello_addr,
HELLO_CHARS.len(),
);
}

for i in 0..5 {
let curr_char: u8 = unsafe { *(hello_addr.wrapping_add(i)) };
if curr_char != HELLO_CHARS[i] {
panic!(
"incorrect character at index {}! Expected {} got {}",
i, HELLO_CHARS[i] as char, curr_char as char
);
}
}

println!("signal handler test succeeded!");

//TODO: add kill and kill unblockable tests

exit(15);
}
1 change: 1 addition & 0 deletions crates/kernel/src/event/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ unsafe impl Sync for AllCores {}

/// The register context of a thread.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Context {
pub regs: [usize; 31],
pub kernel_sp: usize,
Expand Down
66 changes: 65 additions & 1 deletion crates/kernel/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ use alloc::boxed::Box;
use context::{deschedule_thread, Context, DescheduleAction, CORES};
use scheduler::{Priority, Scheduler};

use crate::process::signal::{SignalCode, SignalFlagOptions};
use crate::syscall::proc::exit_user_thread;

pub static SCHEDULER: Scheduler = Scheduler::new();

pub struct Event {
Expand Down Expand Up @@ -68,7 +71,68 @@ pub unsafe extern "C" fn run_event_loop() -> ! {
EventKind::Function(func) => {
func();
}
EventKind::ScheduleThread(thread) => {
EventKind::ScheduleThread(mut thread) => {

if thread.is_user_thread() {
let proc = thread.process.as_ref().unwrap();

//Cleanup, if not in handler then invalidate backup
if !proc.signal_flags.contains(SignalFlagOptions::IN_HANDLER) && thread.backup_context.is_some() {
thread.backup_context = None;
}

if proc.signal_flags.contains(SignalFlagOptions::IS_DEAD) {
unsafe { exit_user_thread(thread, SignalCode::KillUnblockable.into()) };
} else if proc.signal_flags.contains(SignalFlagOptions::IS_KILL) {

if proc.signal_flags.contains(SignalFlagOptions::IN_HANDLER) {
//Received kill while in another signal handler
unsafe { exit_user_thread(thread, SignalCode::InHandler.into()) };
}

if let Some(kill_block_handler) = proc.signal_handlers.lock().kill_block_handler {
//enter_thread will now use the backup context
proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true);

thread.backup_context = Some(thread.context.unwrap());
//replacing link register with the address of the handler and the
//the first two registers with the signal number and stack pointer
thread.backup_context.unwrap().regs[0] = SignalCode::KillBlockable as usize;
thread.backup_context.unwrap().regs[30] = unsafe { core::mem::transmute::<fn(), usize>(kill_block_handler) };

//It is up to sigreturn to remove handler status which will then
//invalidate the secondary context

} else {
//No kill block handler registered
unsafe { exit_user_thread(thread, SignalCode::KillBlockable.into()) };
}
} else if proc.signal_flags.contains(SignalFlagOptions::IS_PAGE_FAULT) {
//There should be a nicer way to write this with less code duplication

if proc.signal_flags.contains(SignalFlagOptions::IN_HANDLER) {
//Received kill while in another signal handler
unsafe { exit_user_thread(thread, SignalCode::InHandler.into()) };
}

if let Some(page_fault_handler) = proc.signal_handlers.lock().user_page_fault_handler {
//enter_thread will now use the backup context
proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true);

thread.backup_context = Some(thread.context.unwrap());
//replacing link register with the address of the handler and the
//the first two registers with the signal number and stack pointer
thread.backup_context.unwrap().regs[0] = SignalCode::PageFault as usize;
//TODO: save the fault address into register 1
thread.backup_context.unwrap().regs[30] = unsafe { core::mem::transmute::<fn(), usize>(page_fault_handler) };

} else {
//No page fault handler registed
unsafe { exit_user_thread(thread, SignalCode::PageFault.into()) };
}
}
}

unsafe { thread.enter_thread() };
}
EventKind::AsyncTask(task_id) => {
Expand Down
15 changes: 14 additions & 1 deletion crates/kernel/src/event/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ use super::{Event, SCHEDULER};
/// while the thread isn't running, stores the saved register state of
/// the thread.
pub struct Thread {
//Pointer to the last context run on this thread. More documentation is offered below
pub last_context: NonNull<Context>,
pub stack: NonNull<[u128]>,
// Stored on the thread's stack
func: Option<NonNull<dyn Callback + Send>>,

pub context: Option<Context>,
//Backup user context which stores the old context prior to the run of the handler
//This is necessary because we do not want to context to be modified inside of the handler
pub backup_context: Option<Context>,
pub user_regs: Option<UserRegs>,
pub process: Option<crate::process::ProcessRef>,
pub priority: Priority,
Expand Down Expand Up @@ -74,6 +78,7 @@ impl Thread {
last_context: NonNull::dangling(),
func: None,
context: Some(data),
backup_context: None,
user_regs: Some(UserRegs {
ttbr0_el1: process.get_ttbr0(),
usermode: true,
Expand Down Expand Up @@ -118,6 +123,7 @@ impl Thread {
last_context: context,
func,
context: None,
backup_context: None,
user_regs: None,
process: None,
priority,
Expand Down Expand Up @@ -176,7 +182,14 @@ impl Thread {
unsafe { crate::sync::disable_interrupts() };

if let Some(user) = &self.user_regs {
let ctx = unsafe { &mut *next_ctx };
//If in handler, use backup context as to not modify regular context
let proc = self.process.as_ref().unwrap();
let ctx: &mut Context = if proc.signal_flags.contains(crate::process::signal::SignalFlagOptions::IN_HANDLER) {
&mut self.backup_context.unwrap()
} else {
unsafe { &mut *next_ctx }
};

unsafe { Self::restore_user_regs(user, ctx) };
}

Expand Down
7 changes: 7 additions & 0 deletions crates/kernel/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::sync::SpinLock;

pub mod fd;
pub mod mem;
pub mod signal;

pub type ProcessRef = Arc<Process>;

Expand All @@ -22,6 +23,8 @@ pub struct Process {
pub root: Option<fd::ArcFd>,
pub file_descriptors: SpinLock<FileDescriptorList>,
pub exit_code: Arc<BlockingOnceCell<ExitStatus>>,
pub signal_handlers: SpinLock<signal::SignalHandlers>,
pub signal_flags: signal::SignalFlags, // TODO: replace this with signal queue
}

impl Process {
Expand All @@ -33,6 +36,8 @@ impl Process {
root: None,
file_descriptors: SpinLock::new(FileDescriptorList { desc: Vec::new() }),
exit_code: Arc::new(BlockingOnceCell::new()),
signal_handlers: SpinLock::new(signal::SignalHandlers::new()),
signal_flags: signal::SignalFlags::empty(),
}
}

Expand Down Expand Up @@ -61,6 +66,8 @@ impl Process {
root: self.root.clone(),
file_descriptors: SpinLock::new(new_fds),
exit_code: Arc::new(BlockingOnceCell::new()),
signal_handlers: SpinLock::new(signal::SignalHandlers::new()),
signal_flags: signal::SignalFlags::empty(),
};

new_process
Expand Down
40 changes: 29 additions & 11 deletions crates/kernel/src/process/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ use crate::arch::memory::vmm::{
use crate::event::async_handler::{run_async_handler, HandlerContext};
use crate::event::context::Context;
use crate::event::exceptions::DataAbortISS;
use crate::process::signal::{SignalFlagOptions, SignalFlags};
use crate::syscall::proc::exit_user_thread;

use super::fd::ArcFd;
use super::signal;

#[derive(Debug)]
pub enum MmapError {
Expand Down Expand Up @@ -310,6 +312,10 @@ unsafe impl Sync for UserAddrSpace {}
pub fn page_fault_handler(ctx: &mut Context, far: usize, _iss: DataAbortISS) -> *mut Context {
run_async_handler(ctx, async move |mut context: HandlerContext<'_>| {
let proc = context.cur_process().unwrap();

if proc.signal_flags.contains(signal::SignalFlagOptions::IN_HANDLER) {
println!("Page fault at addr {far:#10x} while in a signal handler");
}

// TODO: make sure misaligned loads don't loop here?
let page_addr = (far / PAGE_SIZE) * PAGE_SIZE;
Expand All @@ -318,17 +324,29 @@ pub fn page_fault_handler(ctx: &mut Context, far: usize, _iss: DataAbortISS) ->
let vme = mem.get_vme(page_addr);
match vme {
None => {
let exit_code = &proc.exit_code;
exit_code.set(crate::process::ExitStatus {
status: -1i32 as u32,
});
drop(mem);

println!("Invalid user access at addr {far:#10x}");
println!("{:#?}", &*context.regs());

let thread = context.detach_thread();
unsafe { exit_user_thread(thread, -4i32 as u32) }

if let Some(_user_page_fault_handler) = proc.signal_handlers.lock().user_page_fault_handler {
//The event loop handle the rest
proc.signal_flags.set(SignalFlagOptions::IN_HANDLER, true);

//It is up to sigreturn to remove handler status and invalidate the secondary
//context
drop(mem);
context.resume_final()
} else {

let exit_code = &proc.exit_code;
exit_code.set(crate::process::ExitStatus {
status: signal::SignalCode::PageFault.into(),
});
drop(mem);

println!("Invalid user access at addr {far:#10x}");
println!("{:#?}", &*context.regs());

let thread = context.detach_thread();
unsafe { exit_user_thread(thread, -4i32 as u32) }
}
}
Some(vme) => {
mem.populate_page(vme, page_addr).await.unwrap(); // TODO: errors?
Expand Down
Loading