diff --git a/src/syscall/futex.rs b/src/syscall/futex.rs index 2c195582fc..b11bc59f23 100644 --- a/src/syscall/futex.rs +++ b/src/syscall/futex.rs @@ -2,20 +2,21 @@ //! Futex or Fast Userspace Mutex is "a method for waiting until a certain condition becomes true." //! //! For more information about futexes, please read [this](https://eli.thegreenplace.net/2018/basics-of-futexes/) blog post, and the [futex(2)](http://man7.org/linux/man-pages/man2/futex.2.html) man page -use alloc::sync::Arc; use alloc::collections::VecDeque; +use alloc::sync::Arc; use core::intrinsics; -use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use spin::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use rmm::Arch; -use crate::context::{self, Context}; -use crate::time; +use crate::context::{self, memory::AddrSpace, Context}; use crate::memory::PhysicalAddress; -use crate::paging::VirtualAddress; +use crate::paging::{Page, VirtualAddress}; +use crate::time; + use crate::syscall::data::TimeSpec; -use crate::syscall::error::{Error, Result, ESRCH, EAGAIN, EFAULT, EINVAL}; -use crate::syscall::flag::{FUTEX_WAIT, FUTEX_WAIT64, FUTEX_WAKE, FUTEX_REQUEUE}; +use crate::syscall::error::{Error, Result, EAGAIN, EFAULT, EINVAL, ESRCH}; +use crate::syscall::flag::{FUTEX_REQUEUE, FUTEX_WAIT, FUTEX_WAIT64, FUTEX_WAKE}; use crate::syscall::validate::validate_array; type FutexList = VecDeque; @@ -25,42 +26,30 @@ pub struct FutexEntry { context_lock: Arc>, } -/// Fast userspace mutex list -static FUTEXES: Once> = Once::new(); +// TODO: Process-private futexes? In that case, put the futex table in each AddrSpace. +// TODO: Hash table? +static FUTEXES: RwLock = RwLock::new(FutexList::new()); -/// Initialize futexes, called if needed -fn init_futexes() -> RwLock { - RwLock::new(VecDeque::new()) -} +fn validate_and_translate_virt(space: &AddrSpace, addr: VirtualAddress) -> Option { + // TODO: Move this elsewhere! + if addr.data().saturating_add(core::mem::size_of::()) >= crate::USER_END_OFFSET { + return None; + } -/// Get the global futexes list, const -pub fn futexes() -> RwLockReadGuard<'static, FutexList> { - FUTEXES.call_once(init_futexes).read() -} + let page = Page::containing_address(addr); + let off = addr.data() - page.start_address().data(); -/// Get the global futexes list, mutable -pub fn futexes_mut() -> RwLockWriteGuard<'static, FutexList> { - FUTEXES.call_once(init_futexes).write() + let (frame, _) = space.table.utable.translate(page.start_address())?; + + Some(frame.add(off)) } pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> Result { let addr_space = Arc::clone(context::current()?.read().addr_space()?); - let (target_physaddr, _) = { - let virtual_address = VirtualAddress::new(addr); - - if !crate::CurrentRmmArch::virt_is_valid(virtual_address) { - return Err(Error::new(EFAULT)); - } - // TODO: Use this all over the code, making sure that no user pointers that are higher half - // can get to the page table walking procedure. - #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] - if virtual_address.data() & (1 << 63) == (1 << 63) { - return Err(Error::new(EFAULT)); - } - - addr_space.read().table.utable.translate(virtual_address).ok_or(Error::new(EFAULT))? - }; + let target_physaddr = + validate_and_translate_virt(&*addr_space.read(), VirtualAddress::new(addr)) + .ok_or(Error::new(EFAULT))?; match op { // TODO: FUTEX_WAIT_MULTIPLE? @@ -75,28 +64,31 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R }; { - let mut futexes = futexes_mut(); + let mut futexes = FUTEXES.write(); - let context_lock = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - Arc::clone(context_lock) - }; + let context_lock = context::current()?; - // TODO: Is the implicit SeqCst ordering too strong here? let (fetched, expected) = if op == FUTEX_WAIT { // Must be aligned, otherwise it could cross a page boundary and mess up the // (simpler) validation we did in the first place. if addr % 4 != 0 { return Err(Error::new(EINVAL)); } - (u64::from(unsafe { intrinsics::atomic_load_seqcst::(addr as *const u32) }), u64::from(val as u32)) + ( + u64::from(unsafe { + intrinsics::atomic_load_seqcst::(addr as *const u32) + }), + u64::from(val as u32), + ) } else { // op == FUTEX_WAIT64 if addr % 8 != 0 { return Err(Error::new(EINVAL)); } - (unsafe { intrinsics::atomic_load_seqcst::(addr as *const u64) }, val as u64) + ( + unsafe { intrinsics::atomic_load_seqcst::(addr as *const u64) }, + val as u64, + ) }; if fetched != expected { return Err(Error::new(EAGAIN)); @@ -107,7 +99,9 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R if let Some(timeout) = timeout_opt { let start = time::monotonic(); - let end = start + (timeout.tv_sec as u128 * time::NANOS_PER_SEC) + (timeout.tv_nsec as u128); + let end = start + + (timeout.tv_sec as u128 * time::NANOS_PER_SEC) + + (timeout.tv_nsec as u128); context.wake = Some(end); } @@ -120,7 +114,9 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R }); } - unsafe { context::switch(); } + unsafe { + context::switch(); + } if timeout_opt.is_some() { let context_lock = { @@ -136,12 +132,12 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R } Ok(0) - }, + } FUTEX_WAKE => { let mut woken = 0; { - let mut futexes = futexes_mut(); + let mut futexes = FUTEXES.write(); let mut i = 0; @@ -160,29 +156,17 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R } Ok(woken) - }, + } FUTEX_REQUEUE => { - let (addr2_physaddr, _) = { - let addr2_virt = VirtualAddress::new(addr2); - - if !crate::CurrentRmmArch::virt_is_valid(addr2_virt) { - return Err(Error::new(EFAULT)); - } - - // TODO - #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] - if addr2_virt.data() & (1 << 63) == (1 << 63) { - return Err(Error::new(EFAULT)); - } - - addr_space.read().table.utable.translate(addr2_virt).ok_or(Error::new(EFAULT))? - }; + let addr2_physaddr = + validate_and_translate_virt(&*addr_space.read(), VirtualAddress::new(addr2)) + .ok_or(Error::new(EFAULT))?; let mut woken = 0; let mut requeued = 0; { - let mut futexes = futexes_mut(); + let mut futexes = FUTEXES.write(); let mut i = 0; while i < futexes.len() && woken < val { @@ -204,7 +188,7 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R } Ok(woken) - }, - _ => Err(Error::new(EINVAL)) + } + _ => Err(Error::new(EINVAL)), } }