diff --git a/Cargo.lock b/Cargo.lock index e95e1c36bd..520d4c544f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -370,6 +370,7 @@ checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7" name = "redox-rt" version = "0.1.0" dependencies = [ + "bitflags", "goblin", "plain", "redox_syscall", diff --git a/redox-rt/Cargo.toml b/redox-rt/Cargo.toml index 56cecb9b3d..210e5b8884 100644 --- a/redox-rt/Cargo.toml +++ b/redox-rt/Cargo.toml @@ -9,6 +9,7 @@ description = "Libc-independent runtime for Redox" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -redox_syscall = "0.5.1" +bitflags = "2" goblin = { version = "0.7", default-features = false, features = ["elf32", "elf64", "endian_fd"] } plain = "0.2" +redox_syscall = "0.5.1" diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs index 7ec7ca9d3b..257ea43129 100644 --- a/redox-rt/src/lib.rs +++ b/redox-rt/src/lib.rs @@ -31,4 +31,5 @@ pub mod proc; pub mod auxv_defs; pub mod signal; +pub mod sync; pub mod thread; diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs index fcf2a5c309..608aaa683b 100644 --- a/redox-rt/src/proc.rs +++ b/redox-rt/src/proc.rs @@ -709,8 +709,7 @@ pub fn create_set_addr_space_buf( /// descriptors from other schemes are reobtained with `dup`, and grants referencing such file /// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW. pub fn fork_impl() -> Result { - let mut old_mask = 0_u64; - crate::signal::set_sigmask(None, Some(&mut old_mask))?; + let mut old_mask = crate::signal::get_sigmask()?; let pid = unsafe { Error::demux(__relibc_internal_fork_wrapper())? }; if pid == 0 { diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 4247f81702..0d628506f7 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -1,9 +1,11 @@ use core::cell::Cell; use core::ffi::c_int; +use core::sync::atomic::{AtomicU64, Ordering}; -use syscall::{Result, Sigcontrol}; +use syscall::{Result, Sigcontrol, SIGCHLD, SIGCONT, SIGURG, SIGWINCH}; use crate::arch::*; +use crate::sync::Mutex; #[cfg(target_arch = "x86_64")] static CPUID_EAX1_ECX: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); @@ -24,28 +26,6 @@ pub fn sighandler_function() -> usize { } } -pub fn setup_sighandler(control: &Sigcontrol) { - #[cfg(target_arch = "x86_64")] - { - let cpuid_eax1_ecx = unsafe { core::arch::x86_64::__cpuid(1) }.ecx; - CPUID_EAX1_ECX.store(cpuid_eax1_ecx, core::sync::atomic::Ordering::Relaxed); - } - - let data = syscall::SetSighandlerData { - user_handler: sighandler_function(), - excp_handler: 0, // TODO - word_addr: control as *const Sigcontrol as usize, - }; - - let fd = syscall::open( - "thisproc:current/sighandler", - syscall::O_WRONLY | syscall::O_CLOEXEC, - ) - .expect("failed to open thisproc:current/sighandler"); - syscall::write(fd, &data).expect("failed to write to thisproc:current/sighandler"); - let _ = syscall::close(fd); -} - #[repr(C)] pub struct SigStack { #[cfg(target_arch = "x86_64")] @@ -72,19 +52,61 @@ unsafe extern "fastcall" fn inner_fastcall(stack: usize) { inner(&mut *(stack as *mut SigStack)) } +pub fn get_sigmask() -> Result { + let mut mask = 0; + modify_sigmask(Some(&mut mask), Option:: u32>::None)?; + Ok(mask) +} pub fn set_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { - todo!() + modify_sigmask(old, new.map(move |newmask| move |_, upper| if upper { newmask >> 32 } else { newmask } as u32)) } pub fn or_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { - todo!() + // Parsing nightmare... + modify_sigmask(old, new.map(move |newmask| move |oldmask, upper| oldmask | if upper { newmask >> 32 } else { newmask } as u32)) } pub fn andn_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { - todo!() + modify_sigmask(old, new.map(move |newmask| move |oldmask, upper| oldmask & !if upper { newmask >> 32 } else { newmask } as u32)) +} +fn modify_sigmask(old: Option<&mut u64>, op: Option u32>) -> Result<()> { + let _guard = tmp_disable_signals(); + let ctl = current_sigctl(); + + let mut words = ctl.word.each_ref().map(|w| w.load(Ordering::Relaxed)); + + if let Some(old) = old { + *old = combine_mask(words); + } + let Some(mut op) = op else { + return Ok(()); + }; + + let mut can_raise = 0; + let mut cant_raise = 0; + + for i in 0..2 { + while let Err(changed) = ctl.word[i].compare_exchange(words[i], ((words[i] >> 32) << 32) | u64::from(op(words[i] as u32, i == 1)), Ordering::Relaxed, Ordering::Relaxed) { + // If kernel observed a signal being unblocked and pending simultaneously, it will have + // set a flag causing it to check for the INHIBIT_SIGNALS flag every time the context + // is switched to. To avoid race conditions, we should NOT auto-raise those signals in + // userspace as a result of unblocking it. The kernel will instead take care of that later. + can_raise |= (changed & (changed >> 32)) << (32 * i); + cant_raise |= (changed & !(changed >> 32)) << (32 * i); + + words[i] = changed; + } + } + + // TODO: Prioritize cant_raise realtime signals? + + Ok(()) } extern "C" { pub fn __relibc_internal_get_sigcontrol_addr() -> &'static Sigcontrol; } +fn current_sigctl() -> &'static Sigcontrol { + unsafe { __relibc_internal_get_sigcontrol_addr() } +} pub struct TmpDisableSignalsGuard { _inner: () } @@ -114,3 +136,83 @@ impl Drop for TmpDisableSignalsGuard { } } } + +#[derive(Clone, Copy)] +struct SignalAction { + handler: SignalHandler, + flags: SigactionFlags, + mask: u64, +} +bitflags::bitflags! { + #[derive(Clone, Copy)] + struct SigactionFlags: u32 { + const SA_RESETHAND = 1; + } +} +fn default_term_handler(sig: c_int) { + syscall::exit((sig as usize) << 8); +} +fn default_core_handler(sig: c_int) { + syscall::exit((sig as usize) << 8); +} +fn default_ign_handler(_: c_int) { +} +fn stop_handler_sentinel(_: c_int) { +} + +#[derive(Clone, Copy)] +union SignalHandler { + sa_handler: Option, + sa_sigaction: Option, +} + +struct TheDefault { + actions: [SignalAction; 64], + ignmask: u64, +} + +static SIGACTIONS: Mutex<[SignalAction; 64]> = Mutex::new([SignalAction { + handler: SignalHandler { sa_handler: None }, + flags: SigactionFlags::empty(), + mask: 0, +}; 64]); +static IGNMASK: AtomicU64 = AtomicU64::new(sig_bit(SIGCHLD) | sig_bit(SIGURG) | sig_bit(SIGWINCH) | sig_bit(SIGCONT)); + +fn expand_mask(mask: u64) -> [u64; 2] { + [mask & 0xffff_ffff, mask >> 32] +} +fn combine_mask([lo, hi]: [u64; 2]) -> u64 { + lo | ((hi & 0xffff_ffff) << 32) +} + +const fn sig_bit(sig: usize) -> u64 { + //assert_ne!(sig, 32); + //assert_ne!(sig, 0); + 1 << (sig - 1) +} + +pub fn setup_sighandler(control: &Sigcontrol) { + { + let mut sigactions = SIGACTIONS.lock(); + } + + #[cfg(target_arch = "x86_64")] + { + let cpuid_eax1_ecx = unsafe { core::arch::x86_64::__cpuid(1) }.ecx; + CPUID_EAX1_ECX.store(cpuid_eax1_ecx, core::sync::atomic::Ordering::Relaxed); + } + + let data = syscall::SetSighandlerData { + user_handler: sighandler_function(), + excp_handler: 0, // TODO + word_addr: control as *const Sigcontrol as usize, + }; + + let fd = syscall::open( + "thisproc:current/sighandler", + syscall::O_WRONLY | syscall::O_CLOEXEC, + ) + .expect("failed to open thisproc:current/sighandler"); + syscall::write(fd, &data).expect("failed to write to thisproc:current/sighandler"); + let _ = syscall::close(fd); +} diff --git a/redox-rt/src/sync.rs b/redox-rt/src/sync.rs new file mode 100644 index 0000000000..7130778429 --- /dev/null +++ b/redox-rt/src/sync.rs @@ -0,0 +1,48 @@ +// TODO: Share code for simple futex-based mutex between relibc's Mutex<()> and this. + +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut}; +use core::sync::atomic::AtomicU32; + +pub struct Mutex { + pub lockword: AtomicU32, + pub inner: UnsafeCell, +} + +const UNLOCKED: u32 = 0; +const LOCKED: u32 = 1; +const WAITING: u32 = 2; + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +impl Mutex { + pub const fn new(t: T) -> Self { + Self { + lockword: AtomicU32::new(0), + inner: UnsafeCell::new(t), + } + } + pub fn lock(&self) -> MutexGuard<'_, T> { + MutexGuard { lock: self } + } +} +pub struct MutexGuard<'l, T> { + lock: &'l Mutex, +} +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.inner.get() } + } +} +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.lock.inner.get() } + } +} +impl Drop for MutexGuard<'_, T> { + fn drop(&mut self) { + } +}