Add support for FUTEX_WAIT64.
This commit is contained in:
2
rmm
2
rmm
Submodule rmm updated: c66956ca2a...32caee3095
@@ -41,6 +41,8 @@ pub mod start;
|
||||
/// Stop function
|
||||
pub mod stop;
|
||||
|
||||
pub use ::rmm::X8664Arch as CurrentRmmArch;
|
||||
|
||||
// Flags
|
||||
pub mod flags {
|
||||
pub const FLAG_SINGLESTEP: usize = 1 << 8;
|
||||
|
||||
@@ -7,14 +7,16 @@ use alloc::collections::VecDeque;
|
||||
use core::intrinsics;
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use rmm::Arch;
|
||||
|
||||
use crate::context::{self, Context};
|
||||
use crate::time;
|
||||
use crate::memory::PhysicalAddress;
|
||||
use crate::paging::{ActivePageTable, TableKind, VirtualAddress};
|
||||
use crate::syscall::data::TimeSpec;
|
||||
use crate::syscall::error::{Error, Result, ESRCH, EAGAIN, EFAULT, EINVAL};
|
||||
use crate::syscall::flag::{FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE};
|
||||
use crate::syscall::validate::{validate_slice, validate_slice_mut};
|
||||
use crate::syscall::flag::{FUTEX_WAIT, FUTEX_WAIT64, FUTEX_WAKE, FUTEX_REQUEUE};
|
||||
use crate::syscall::validate::{validate_array, validate_slice, validate_slice_mut};
|
||||
|
||||
type FutexList = VecDeque<FutexEntry>;
|
||||
|
||||
@@ -41,23 +43,28 @@ pub fn futexes_mut() -> RwLockWriteGuard<'static, FutexList> {
|
||||
FUTEXES.call_once(init_futexes).write()
|
||||
}
|
||||
|
||||
// FIXME: Don't take a mutable reference to the addr, since rustc can make assumptions that the
|
||||
// pointee cannot be changed by another thread, which could make atomic ops useless.
|
||||
pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) -> Result<usize> {
|
||||
pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> Result<usize> {
|
||||
let target_physaddr = unsafe {
|
||||
let active_table = ActivePageTable::new(TableKind::User);
|
||||
let virtual_address = VirtualAddress::new(addr as *mut i32 as usize);
|
||||
let virtual_address = VirtualAddress::new(addr);
|
||||
|
||||
if !crate::CurrentRmmArch::virt_is_valid(virtual_address) || crate::CurrentRmmArch::virt_kind(virtual_address) == TableKind::Kernel {
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
|
||||
// FIXME: Already validated in syscall/mod.rs
|
||||
active_table.translate(virtual_address).ok_or(Error::new(EFAULT))?
|
||||
};
|
||||
|
||||
match op {
|
||||
FUTEX_WAIT => {
|
||||
let timeout_opt = if val2 != 0 {
|
||||
Some(validate_slice(val2 as *const TimeSpec, 1).map(|req| &req[0])?)
|
||||
} else {
|
||||
// TODO: FUTEX_WAIT_MULTIPLE?
|
||||
FUTEX_WAIT | FUTEX_WAIT64 => {
|
||||
let timeout_ptr = val2 as *const TimeSpec;
|
||||
|
||||
let timeout_opt = if timeout_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
let [timeout] = unsafe { *validate_array(timeout_ptr)? };
|
||||
Some(timeout)
|
||||
};
|
||||
|
||||
{
|
||||
@@ -69,7 +76,22 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32)
|
||||
Arc::clone(&context_lock)
|
||||
};
|
||||
|
||||
if unsafe { intrinsics::atomic_load(addr) != val } {
|
||||
// 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::<u32>(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::<u64>(addr as *const u64) }, val as u64)
|
||||
};
|
||||
if fetched != expected {
|
||||
return Err(Error::new(EAGAIN));
|
||||
}
|
||||
|
||||
@@ -116,15 +138,17 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32)
|
||||
let mut futexes = futexes_mut();
|
||||
|
||||
let mut i = 0;
|
||||
while i < futexes.len() && (woken as i32) < val {
|
||||
if futexes[i].target_physaddr == target_physaddr {
|
||||
if let Some(futex) = futexes.swap_remove_back(i) {
|
||||
let mut context_guard = futex.context_lock.write();
|
||||
context_guard.unblock();
|
||||
woken += 1;
|
||||
}
|
||||
} else {
|
||||
|
||||
// TODO: Use retain, once it allows the closure to tell it to stop iterating...
|
||||
while i < futexes.len() && woken < val {
|
||||
if futexes[i].target_physaddr != target_physaddr {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if let Some(futex) = futexes.swap_remove_back(i) {
|
||||
let mut context_guard = futex.context_lock.write();
|
||||
context_guard.unblock();
|
||||
woken += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,12 +157,13 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32)
|
||||
},
|
||||
FUTEX_REQUEUE => {
|
||||
let addr2_physaddr = unsafe {
|
||||
let addr2_virt = VirtualAddress::new(addr2);
|
||||
|
||||
if !crate::CurrentRmmArch::virt_is_valid(addr2_virt) || crate::CurrentRmmArch::virt_kind(addr2_virt) == TableKind::Kernel {
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
|
||||
let active_table = ActivePageTable::new(TableKind::User);
|
||||
|
||||
let addr2_safe = validate_slice_mut(addr2, 1).map(|addr2_safe| &mut addr2_safe[0])?;
|
||||
let addr2_virt = VirtualAddress::new(addr2_safe as *mut i32 as usize);
|
||||
|
||||
// FIXME: Already validated.
|
||||
active_table.translate(addr2_virt).ok_or(Error::new(EFAULT))?
|
||||
};
|
||||
|
||||
@@ -149,22 +174,21 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32)
|
||||
let mut futexes = futexes_mut();
|
||||
|
||||
let mut i = 0;
|
||||
while i < futexes.len() && (woken as i32) < val {
|
||||
if futexes[i].target_physaddr == target_physaddr {
|
||||
if let Some(futex) = futexes.swap_remove_back(i) {
|
||||
futex.context_lock.write().unblock();
|
||||
woken += 1;
|
||||
}
|
||||
} else {
|
||||
while i < futexes.len() && woken < val {
|
||||
if futexes[i].target_physaddr != target_physaddr {
|
||||
i += 1;
|
||||
}
|
||||
if let Some(futex) = futexes.swap_remove_back(i) {
|
||||
futex.context_lock.write().unblock();
|
||||
woken += 1;
|
||||
}
|
||||
}
|
||||
while i < futexes.len() && requeued < val2 {
|
||||
if futexes[i].target_physaddr == target_physaddr {
|
||||
futexes[i].target_physaddr = addr2_physaddr;
|
||||
requeued += 1;
|
||||
if futexes[i].target_physaddr != target_physaddr {
|
||||
i += 1;
|
||||
}
|
||||
i += 1;
|
||||
futexes[i].target_physaddr = addr2_physaddr;
|
||||
requeued += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, bp: u
|
||||
}
|
||||
),
|
||||
SYS_CLOCK_GETTIME => clock_gettime(b, validate_slice_mut(c as *mut TimeSpec, 1).map(|time| &mut time[0])?),
|
||||
SYS_FUTEX => futex(validate_slice_mut(b as *mut i32, 1).map(|uaddr| &mut uaddr[0])?, c, d as i32, e, f as *mut i32),
|
||||
SYS_FUTEX => futex(b, c, d, e, f),
|
||||
SYS_GETPID => getpid().map(ContextId::into),
|
||||
SYS_GETPGID => getpgid(ContextId::from(b)).map(ContextId::into),
|
||||
SYS_GETPPID => getppid().map(ContextId::into),
|
||||
|
||||
@@ -41,9 +41,26 @@ pub fn validate_slice<T>(ptr: *const T, len: usize) -> Result<&'static [T]> {
|
||||
Ok(unsafe { slice::from_raw_parts(ptr, len) })
|
||||
}
|
||||
}
|
||||
/// Convert a pointer with fixed static length to a reference to an array, if valid.
|
||||
// TODO: This is probably also quite unsafe, mainly because we have no idea unless we do very
|
||||
// careful checking, that this upholds the rules that LLVM relies with shared references, namely
|
||||
// that the value cannot change by others. Atomic volatile.
|
||||
pub unsafe fn validate_array<'a, T, const N: usize>(ptr: *const T) -> Result<&'a [T; N]> {
|
||||
validate(ptr as usize, mem::size_of::<T>() * N, false)?;
|
||||
Ok(&*ptr.cast::<[T; N]>())
|
||||
}
|
||||
pub unsafe fn validate_array_mut<'a, T, const N: usize>(ptr: *mut T) -> Result<&'a mut [T; N]> {
|
||||
validate(ptr as usize, mem::size_of::<T>() * N, true)?;
|
||||
Ok(&mut *ptr.cast::<[T; N]>())
|
||||
}
|
||||
|
||||
/// Convert a pointer and length to slice, if valid
|
||||
//TODO: Mark unsafe
|
||||
// TODO: Mark unsafe
|
||||
//
|
||||
// FIXME: This is probably never ever safe, except under very special circumstances. Any &mut
|
||||
// reference will allow LLVM to assume that nobody else will ever modify this value, which is
|
||||
// certainly not the case for multithreaded userspace programs. Instead, we will want something
|
||||
// like atomic volatile.
|
||||
pub fn validate_slice_mut<T>(ptr: *mut T, len: usize) -> Result<&'static mut [T]> {
|
||||
if len == 0 {
|
||||
Ok(&mut [])
|
||||
|
||||
2
syscall
2
syscall
Submodule syscall updated: 52fcd238db...fa5bc90ead
Reference in New Issue
Block a user