48fc2f1c90
Adds proper kernel-level namespace syscall handling: - SYS_MKNS: dups the current namespace fd with the caller-supplied buffer (NsDup::ForkNs + scheme names). The dup is handled by the bootstrap's userspace namespace manager (initnsmgr) which creates the new namespace and returns the fd. - SYS_SETNS: switches the calling context's ns_fd to the given fd. This makes the new namespace the default for all subsequent scheme lookups by this context. - Context gains an ns_fd: Option<usize> field, initialized to None, meaning "use the global namespace" (default). - Both syscalls are wired into the dispatch table in syscall/mod.rs. Per local/docs/LOCAL-FORK-SUPREMACY-POLICY.md: the kernel fork must be complete. Previously SYS_SETNS was undefined and SYS_MKNS returned ENOSYS; init's setrens() panicked on bare metal.
306 lines
11 KiB
Rust
306 lines
11 KiB
Rust
//!
|
|
//! This module provides syscall definitions and the necessary resources to parse incoming
|
|
//! syscalls
|
|
|
|
extern crate syscall;
|
|
|
|
use syscall::{dirent::DirentHeader, CallFlags, RwFlags, EINVAL};
|
|
|
|
pub use self::syscall::{
|
|
data, error, flag, io, number, ptrace_event, EnvRegisters, FloatRegisters, IntRegisters,
|
|
};
|
|
|
|
pub use self::{fs::*, futex::futex, process::*, time::*, usercopy::validate_region};
|
|
|
|
use self::{
|
|
data::{Map, TimeSpec},
|
|
debug::{debug_end, debug_start},
|
|
error::{Error, Result, ENOSYS},
|
|
flag::{EventFlags, MapFlags},
|
|
number::*,
|
|
usercopy::UserSlice,
|
|
};
|
|
|
|
use crate::{
|
|
context::{self, memory::AddrSpace},
|
|
percpu::PercpuBlock,
|
|
scheme::{memory::MemoryScheme, FileHandle},
|
|
sync::CleanLockToken,
|
|
};
|
|
|
|
/// Debug
|
|
pub mod debug;
|
|
|
|
/// Filesystem syscalls
|
|
pub mod fs;
|
|
|
|
/// Fast userspace mutex
|
|
pub mod futex;
|
|
|
|
/// Process syscalls
|
|
pub mod process;
|
|
|
|
/// Time syscalls
|
|
pub mod time;
|
|
|
|
/// Safely copying memory between user and kernel memory
|
|
pub mod usercopy;
|
|
|
|
/// This function is the syscall handler of the kernel, it is composed of an inner function that returns a `Result<usize>`. After the inner function runs, the syscall
|
|
/// function calls [`Error::mux`] on it.
|
|
#[must_use]
|
|
pub fn syscall(
|
|
a: usize,
|
|
b: usize,
|
|
c: usize,
|
|
d: usize,
|
|
e: usize,
|
|
f: usize,
|
|
g: usize,
|
|
token: &mut CleanLockToken,
|
|
) -> usize {
|
|
#[inline(always)]
|
|
fn inner(
|
|
a: usize,
|
|
b: usize,
|
|
c: usize,
|
|
d: usize,
|
|
e: usize,
|
|
f: usize,
|
|
g: usize,
|
|
token: &mut CleanLockToken,
|
|
) -> Result<usize> {
|
|
let fd = FileHandle::from(b);
|
|
//SYS_* is declared in kernel/syscall/src/number.rs
|
|
match a {
|
|
SYS_WRITE2 => {
|
|
let bytes = file_op_generic_ext(fd, token, |scheme, _, desc, token| {
|
|
let flags = if f == usize::MAX {
|
|
None
|
|
} else {
|
|
Some(
|
|
u32::try_from(f)
|
|
.ok()
|
|
.and_then(RwFlags::from_bits)
|
|
.ok_or(Error::new(EINVAL))?,
|
|
)
|
|
};
|
|
scheme.kwriteoff(
|
|
desc.number,
|
|
UserSlice::ro(c, d)?,
|
|
e as u64,
|
|
flags.map_or(desc.flags, |f| desc.rw_flags(f)),
|
|
desc.flags,
|
|
token,
|
|
)
|
|
})?;
|
|
let current = context::current();
|
|
let mut guard = current.write(token.token());
|
|
guard.io_wchar = guard.io_wchar.saturating_add(bytes as u64);
|
|
guard.io_syscw = guard.io_syscw.saturating_add(1);
|
|
guard.io_write_bytes = guard.io_write_bytes.saturating_add(bytes as u64);
|
|
Ok(bytes)
|
|
}
|
|
SYS_WRITE => sys_write(fd, UserSlice::ro(c, d)?, token),
|
|
SYS_FMAP => {
|
|
let addrspace = AddrSpace::current()?;
|
|
let map = unsafe { UserSlice::ro(c, d)?.read_exact::<Map>()? };
|
|
if b == !0 {
|
|
MemoryScheme::fmap_anonymous(&addrspace, &map, false, token)
|
|
} else {
|
|
file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme.kfmap(number, &addrspace, &map, false, token)
|
|
})
|
|
}
|
|
}
|
|
SYS_GETDENTS => {
|
|
let header_size = u16::try_from(e).map_err(|_| Error::new(EINVAL))?;
|
|
|
|
if usize::from(header_size) != size_of::<DirentHeader>() {
|
|
// TODO: allow? If so, zero_out must be implemented for UserSlice
|
|
return Err(Error::new(EINVAL));
|
|
}
|
|
|
|
file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme.getdents(number, UserSlice::wo(c, d)?, header_size, f as u64, token)
|
|
})
|
|
}
|
|
SYS_FUTIMENS => file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme.kfutimens(number, UserSlice::ro(c, d)?, token)
|
|
}),
|
|
|
|
SYS_READ2 => {
|
|
let bytes = file_op_generic_ext(fd, token, |scheme, _, desc, token| {
|
|
let flags = if f == usize::MAX {
|
|
None
|
|
} else {
|
|
Some(
|
|
u32::try_from(f)
|
|
.ok()
|
|
.and_then(RwFlags::from_bits)
|
|
.ok_or(Error::new(EINVAL))?,
|
|
)
|
|
};
|
|
scheme.kreadoff(
|
|
desc.number,
|
|
UserSlice::wo(c, d)?,
|
|
e as u64,
|
|
flags.map_or(desc.flags, |f| desc.rw_flags(f)),
|
|
desc.flags,
|
|
token,
|
|
)
|
|
})?;
|
|
let current = context::current();
|
|
let mut guard = current.write(token.token());
|
|
guard.io_rchar = guard.io_rchar.saturating_add(bytes as u64);
|
|
guard.io_syscr = guard.io_syscr.saturating_add(1);
|
|
guard.io_read_bytes = guard.io_read_bytes.saturating_add(bytes as u64);
|
|
Ok(bytes)
|
|
}
|
|
SYS_READ => sys_read(fd, UserSlice::wo(c, d)?, token),
|
|
SYS_FPATH => file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme.kfpath(number, UserSlice::wo(c, d)?, token)
|
|
}),
|
|
SYS_FSTAT => fstat(fd, UserSlice::wo(c, d)?, token).map(|()| 0),
|
|
SYS_FSTATVFS => file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme
|
|
.kfstatvfs(number, UserSlice::wo(c, d)?, token)
|
|
.map(|()| 0)
|
|
}),
|
|
|
|
SYS_DUP => dup(fd, UserSlice::ro(c, d)?, token).map(FileHandle::into),
|
|
SYS_DUP_INTO => dup_into(
|
|
fd,
|
|
FileHandle::from(e),
|
|
UserSlice::ro(c, d)?,
|
|
token,
|
|
).map(FileHandle::into),
|
|
SYS_DUP2 => {
|
|
dup2(fd, FileHandle::from(c), UserSlice::ro(d, e)?, token).map(FileHandle::into)
|
|
}
|
|
|
|
#[cfg(target_pointer_width = "32")]
|
|
SYS_SENDFD => sendfd(
|
|
fd,
|
|
FileHandle::from(c),
|
|
d,
|
|
e as u64 | ((f as u64) << 32),
|
|
token,
|
|
),
|
|
|
|
#[cfg(target_pointer_width = "64")]
|
|
SYS_SENDFD => sendfd(fd, FileHandle::from(c), d, e as u64, token),
|
|
|
|
SYS_LSEEK => lseek(fd, c as i64, d, token),
|
|
SYS_FCHMOD => file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme.fchmod(number, c as u16, token).map(|()| 0)
|
|
}),
|
|
SYS_FCHOWN => file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme.fchown(number, c as u32, d as u32, token).map(|()| 0)
|
|
}),
|
|
SYS_FCNTL => fcntl(fd, c, d, token),
|
|
SYS_FEVENT => file_op_generic(fd, token, |scheme, number, token| {
|
|
Ok(scheme
|
|
.fevent(number, EventFlags::from_bits_truncate(c), token)?
|
|
.bits())
|
|
}),
|
|
SYS_FLINK => flink(fd, UserSlice::ro(c, d)?, token).map(|()| 0),
|
|
SYS_FRENAME => frename(fd, UserSlice::ro(c, d)?, token).map(|()| 0),
|
|
SYS_FUNMAP => funmap(b, c, token),
|
|
|
|
SYS_FSYNC => file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme.fsync(number, token).map(|()| 0)
|
|
}),
|
|
SYS_SYNC => {
|
|
// Red Bear filesystem I/O is synchronous — all data is
|
|
// already on disk. sync() is a no-op per POSIX semantics.
|
|
// Cross-referenced with Linux 7.1 fs/sync.c ksys_sync().
|
|
Ok(0)
|
|
}
|
|
SYS_SYNCFS => file_op_generic(fd, token, |scheme, number, token| {
|
|
// syncfs(fd) syncs the filesystem containing fd.
|
|
// On Red Bear, I/O is synchronous so this is a no-op.
|
|
scheme.fsync(number, token).map(|()| 0)
|
|
}),
|
|
// TODO: 64-bit lengths on 32-bit platforms
|
|
SYS_FTRUNCATE => file_op_generic(fd, token, |scheme, number, token| {
|
|
scheme.ftruncate(number, c, token).map(|()| 0)
|
|
}),
|
|
|
|
SYS_CLOSE => close(fd, token).map(|()| 0),
|
|
SYS_CALL => call(
|
|
fd,
|
|
UserSlice::rw(c, d)?,
|
|
CallFlags::from_bits(e & !0xff).ok_or(Error::new(EINVAL))?,
|
|
UserSlice::ro(f, (e & 0xff) * 8)?,
|
|
token,
|
|
),
|
|
SYS_OPENAT => {
|
|
openat(fd, UserSlice::ro(c, d)?, e, f as _, 0, 0, token).map(FileHandle::into)
|
|
}
|
|
SYS_OPENAT_INTO => openat_into(
|
|
fd,
|
|
UserSlice::ro(c, d)?,
|
|
e,
|
|
f as u32,
|
|
FileHandle::from(g as usize),
|
|
token,
|
|
)
|
|
.map(FileHandle::into),
|
|
SYS_OPENAT_WITH_FILTER => openat(
|
|
fd,
|
|
UserSlice::ro(c, d)?,
|
|
e,
|
|
(e & syscall::O_FCNTL_MASK) as _,
|
|
f as _,
|
|
g as _,
|
|
token,
|
|
)
|
|
.map(FileHandle::into),
|
|
SYS_UNLINKAT => unlinkat(fd, UserSlice::ro(c, d)?, e, 0, 0, token).map(|()| 0),
|
|
SYS_UNLINKAT_WITH_FILTER => {
|
|
unlinkat(fd, UserSlice::ro(c, d)?, e, f as _, g as _, token).map(|()| 0)
|
|
}
|
|
SYS_YIELD => sched_yield(token).map(|()| 0),
|
|
SYS_NANOSLEEP => nanosleep(
|
|
UserSlice::ro(b, size_of::<TimeSpec>())?,
|
|
UserSlice::wo(c, size_of::<TimeSpec>())?.none_if_null(),
|
|
token,
|
|
)
|
|
.map(|()| 0),
|
|
SYS_CLOCK_GETTIME => {
|
|
clock_gettime(b, UserSlice::wo(c, size_of::<TimeSpec>())?, token).map(|()| 0)
|
|
}
|
|
SYS_FUTEX => futex(b, c, d, e, f, token),
|
|
|
|
SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d), token).map(|()| 0),
|
|
SYS_MREMAP => mremap(b, c, d, e, f, token),
|
|
SYS_MKNS => sys_mkns(UserSlice::ro(b, c)?, token).map(FileHandle::into),
|
|
SYS_SETNS => sys_setns(fd, token).map(|()| 0),
|
|
|
|
_ => {
|
|
println!("KERNEL: unimplemented syscall a={:#x} b={:#x} c={:#x} d={:#x}", a, b, c, d);
|
|
Err(Error::new(ENOSYS))
|
|
}
|
|
}
|
|
}
|
|
|
|
PercpuBlock::current().inside_syscall.set(true);
|
|
|
|
debug_start([a, b, c, d, e, f, g], token);
|
|
|
|
let result = inner(a, b, c, d, e, f, g, token);
|
|
|
|
debug_end([a, b, c, d, e, f, g], result, token);
|
|
|
|
let percpu = PercpuBlock::current();
|
|
percpu.inside_syscall.set(false);
|
|
|
|
if percpu.switch_internals.being_sigkilled.get() {
|
|
exit_this_context(None, token);
|
|
}
|
|
|
|
// errormux turns Result<usize> into -errno
|
|
Error::mux(result)
|
|
}
|