WIP: use proc manager in non-init fork.

This commit is contained in:
4lDO2
2024-12-26 21:28:48 +01:00
parent 91e58e8568
commit b851422214
4 changed files with 164 additions and 75 deletions
+18 -13
View File
@@ -11,8 +11,11 @@ use syscall::{
};
use crate::{
proc::{fork_inner, FdGuard},
signal::{get_sigaltstack, inner_c, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT},
proc::{fork_inner, FdGuard, ForkArgs},
signal::{
get_sigaltstack, inner_c, PosixStackt, RtSigarea, SigStack,
PROC_CONTROL_STRUCT,
},
Tcb,
};
@@ -92,16 +95,16 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> {
Ok(())
}
unsafe extern "sysv64" fn fork_impl(initial_rsp: *mut usize) -> usize {
Error::mux(fork_inner(initial_rsp))
unsafe extern "sysv64" fn fork_impl(initial_rsp: *mut usize, args: &ForkArgs) -> usize {
Error::mux(fork_inner(initial_rsp, args))
}
unsafe extern "sysv64" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) {
unsafe extern "sysv64" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize, new_thr_fd: usize) {
let _ = syscall::close(cur_filetable_fd);
crate::child_hook_common(FdGuard::new(new_pid_fd));
crate::child_hook_common(FdGuard::new(new_pid_fd), FdGuard::new(new_thr_fd));
}
asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
push rbp
mov rbp, rsp
@@ -112,15 +115,16 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
push r14
push r15
sub rsp, 32
sub rsp, 48
stmxcsr [rsp+16]
fnstcw [rsp+24]
stmxcsr [rsp+32]
fnstcw [rsp+40]
mov rdi, rsp
// rsi: &ForkArgs
call {fork_impl}
add rsp, 80
add rsp, 96
pop rbp
ret
@@ -129,10 +133,11 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
asmfunction!(__relibc_internal_fork_ret: ["
mov rdi, [rsp]
mov rsi, [rsp + 8]
mov rdx, [rsp + 16]
call {child_hook}
ldmxcsr [rsp + 16]
fldcw [rsp + 24]
ldmxcsr [rsp + 32]
fldcw [rsp + 40]
xor rax, rax
+55 -15
View File
@@ -10,18 +10,20 @@
#![forbid(unreachable_patterns)]
use core::cell::{SyncUnsafeCell, UnsafeCell};
use core::mem::size_of;
use generic_rt::{ExpectTlsFree, GenericTcb};
use syscall::{Sigcontrol, O_CLOEXEC};
use self::proc::FdGuard;
use self::protocol::ProcMeta;
use self::sync::Mutex;
extern crate alloc;
#[macro_export]
macro_rules! asmfunction(
($name:ident $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => {
($name:ident $(($($arg:ty),*))? $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => {
::core::arch::global_asm!(concat!("
.p2align 4
.section .text.", stringify!($name), ", \"ax\", @progbits
@@ -33,7 +35,7 @@ macro_rules! asmfunction(
"), $($decl = $(sym $symname)?$(const $constval)?),*);
extern "C" {
pub fn $name() $(-> $ret)?;
pub fn $name($($(_: $arg),*)?) $(-> $ret)?;
}
}
);
@@ -45,6 +47,7 @@ pub mod proc;
#[path = "../../src/platform/auxv_defs.rs"]
pub mod auxv_defs;
pub mod protocol;
pub mod signal;
pub mod sync;
pub mod sys;
@@ -135,6 +138,7 @@ pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) {
}
/// Initialize redox-rt in situations where relibc is not used
#[cfg(not(feature = "proc"))]
pub unsafe fn initialize_freestanding() {
// TODO: This code is a hack! Integrate the ld_so TCB code into generic-rt, and then use that
// (this function will need pointers to the ELF structs normally passed in auxvs), so the TCB
@@ -178,24 +182,48 @@ pub unsafe fn initialize_freestanding() {
}
initialize();
}
pub unsafe fn initialize() {
#[cfg(feature = "proc")]
pub(crate) fn read_proc_meta(proc: &FdGuard) -> syscall::Result<ProcMeta> {
let mut bytes = [0_u8; size_of::<ProcMeta>()];
let _ = syscall::read(**proc, &mut bytes)?;
Ok(*plain::from_bytes::<ProcMeta>(&bytes).unwrap())
}
pub unsafe fn initialize(
#[cfg(feature = "proc")]
// Find the PID attached to this process
let (pid, ppid) = todo!("getpid");
proc_fd: FdGuard,
) {
#[cfg(feature = "proc")]
let metadata = read_proc_meta(&proc_fd).unwrap();
#[cfg(not(feature = "proc"))]
// Bootstrap mode, don't associate proc fds with PIDs
let (pid, ppid): (u32, u32) = (0, 0);
let metadata = ProcMeta::default();
STATIC_PROC_INFO.get().write(StaticProcInfo {
pid,
ppid,
pid: metadata.pid,
ppid: metadata.ppid,
#[cfg(feature = "proc")]
proc_fd,
});
#[cfg(feature = "proc")]
{
*DYNAMIC_PROC_INFO.lock() = DynamicProcInfo {
pgid: metadata.pgid,
egid: metadata.egid,
euid: metadata.euid,
ruid: metadata.ruid,
rgid: metadata.rgid,
};
}
}
struct StaticProcInfo {
pub(crate) struct StaticProcInfo {
pid: u32,
ppid: u32,
#[cfg(feature = "proc")]
proc_fd: FdGuard,
}
struct DynamicProcInfo {
@@ -209,6 +237,7 @@ struct DynamicProcInfo {
static STATIC_PROC_INFO: SyncUnsafeCell<StaticProcInfo> = SyncUnsafeCell::new(StaticProcInfo {
pid: 0,
ppid: 0,
proc_fd: FdGuard::new(usize::MAX),
});
static DYNAMIC_PROC_INFO: Mutex<DynamicProcInfo> = Mutex::new(DynamicProcInfo {
pgid: u32::MAX,
@@ -218,13 +247,24 @@ static DYNAMIC_PROC_INFO: Mutex<DynamicProcInfo> = Mutex::new(DynamicProcInfo {
rgid: u32::MAX,
});
unsafe fn child_hook_common(new_pid_fd: FdGuard) {
pub(crate) fn static_proc_info() -> &'static StaticProcInfo {
unsafe { &*STATIC_PROC_INFO.get() }
}
unsafe fn child_hook_common(new_proc_fd: FdGuard, new_thr_fd: FdGuard) {
// TODO: just pass PID to child rather than obtaining it via IPC?
#[cfg(feature = "proc")]
let (pid, ppid): (u32, u32) = todo!("getpid");
#[cfg(not(feature = "proc"))]
let (pid, ppid): (u32, u32) = (0, 0);
let metadata = read_proc_meta(&new_proc_fd).unwrap();
// TODO: Currently pidfd == threadfd, but this will not be the case later.
RtTcb::current().thr_fd.get().write(Some(new_pid_fd));
#[cfg(not(feature = "proc"))]
let metadata = ProcMeta::default();
STATIC_PROC_INFO.get().write(StaticProcInfo {
pid: metadata.pid,
ppid: metadata.ppid,
proc_fd: new_proc_fd,
});
// FIXME
RtTcb::current().thr_fd.get().write(Some(new_thr_fd));
}
+82 -47
View File
@@ -1,6 +1,6 @@
use core::{fmt::Debug, mem::size_of};
use crate::{arch::*, auxv_defs::*, DYNAMIC_PROC_INFO, STATIC_PROC_INFO};
use crate::{arch::*, auxv_defs::*, read_proc_meta, static_proc_info, RtTcb, DYNAMIC_PROC_INFO, STATIC_PROC_INFO};
use alloc::{boxed::Box, collections::BTreeMap, vec};
@@ -23,14 +23,14 @@ use syscall::{
PAGE_SIZE, PROT_EXEC, PROT_READ, PROT_WRITE,
};
pub enum FexecResult {
pub enum FexecResult<'a> {
Normal {
addrspace_handle: FdGuard,
},
Interp {
path: Box<[u8]>,
image_file: FdGuard,
open_via_dup: FdGuard,
thread_fd: &'a FdGuard,
interp_override: InterpOverride,
},
}
@@ -55,9 +55,10 @@ pub struct ExtraInfo<'a> {
pub umask: u32,
}
pub fn fexec_impl<A, E>(
pub fn fexec_impl<'a, A, E>(
image_file: FdGuard,
open_via_dup: FdGuard,
thread_fd: &'a FdGuard,
proc_fd: &FdGuard,
memory_scheme_fd: &FdGuard,
path: &[u8],
args: A,
@@ -65,7 +66,7 @@ pub fn fexec_impl<A, E>(
total_args_envs_size: usize,
extrainfo: &ExtraInfo,
mut interp_override: Option<InterpOverride>,
) -> Result<FexecResult>
) -> Result<FexecResult<'a>>
where
A: IntoIterator,
E: IntoIterator,
@@ -81,7 +82,7 @@ where
let header = Header::from_bytes(&header_bytes);
let grants_fd = {
let current_addrspace_fd = FdGuard::new(syscall::dup(*open_via_dup, b"addrspace")?);
let current_addrspace_fd = FdGuard::new(syscall::dup(**thread_fd, b"addrspace")?);
FdGuard::new(syscall::dup(*current_addrspace_fd, b"empty")?)
};
@@ -134,7 +135,7 @@ where
return Ok(FexecResult::Interp {
path: interp.into_boxed_slice(),
image_file,
open_via_dup,
thread_fd,
interp_override: InterpOverride {
at_entry: header.e_entry as usize,
at_phnum: phnum,
@@ -389,7 +390,7 @@ where
push(argc)?;
if let Ok(sighandler_fd) = syscall::dup(*open_via_dup, b"sighandler").map(FdGuard::new) {
if let Ok(sighandler_fd) = syscall::dup(**thread_fd, b"sighandler").map(FdGuard::new) {
let _ = syscall::write(
*sighandler_fd,
&SetSighandlerData {
@@ -402,11 +403,11 @@ where
}
unsafe {
deactivate_tcb(*open_via_dup)?;
deactivate_tcb(**thread_fd)?;
}
// TODO: Restore old name if exec failed?
if let Ok(name_fd) = syscall::dup(*open_via_dup, b"name").map(FdGuard::new) {
if let Ok(name_fd) = syscall::dup(**thread_fd, b"name").map(FdGuard::new) {
let _ = syscall::write(*name_fd, interp_override.as_ref().map_or(path, |o| &o.name));
}
if interp_override.is_some() {
@@ -416,7 +417,7 @@ where
let _ = syscall::write(*mmap_min_fd, &usize::to_ne_bytes(aligned_last_addr));
}
let addrspace_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-addrspace")?);
let addrspace_selection_fd = FdGuard::new(syscall::dup(**thread_fd, b"current-addrspace")?);
let _ = syscall::write(
*addrspace_selection_fd,
@@ -635,7 +636,7 @@ pub struct FdGuard {
taken: bool,
}
impl FdGuard {
pub fn new(fd: usize) -> Self {
pub const fn new(fd: usize) -> Self {
Self { fd, taken: false }
}
pub fn take(&mut self) -> usize {
@@ -679,9 +680,9 @@ pub fn create_set_addr_space_buf(
/// Spawns a new context which will not share the same address space as the current one. File
/// 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<usize> {
pub fn fork_impl(args: &ForkArgs<'_>) -> Result<usize> {
let old_mask = crate::signal::get_sigmask()?;
let pid = unsafe { Error::demux(__relibc_internal_fork_wrapper())? };
let pid = unsafe { Error::demux(__relibc_internal_fork_wrapper(args as *const ForkArgs as usize))? };
if pid == 0 {
crate::signal::set_sigmask(Some(old_mask), None)?;
@@ -689,36 +690,45 @@ pub fn fork_impl() -> Result<usize> {
Ok(pid)
}
pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
let (cur_filetable_fd, new_pid_fd, new_pid);
pub enum ForkArgs<'a> {
Init { this_thr_fd: &'a FdGuard, auth: &'a FdGuard },
Managed,
}
pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result<usize> {
let (cur_filetable_fd, new_proc_fd, new_thr_fd, new_pid);
{
let cur_pid_fd = FdGuard::new(syscall::open(
"/scheme/thisproc/current",
O_CLOEXEC,
)?);
(new_pid_fd, new_pid) = new_child_process(*cur_pid_fd)?;
let cur_thr_fd = match args {
ForkArgs::Init { this_thr_fd, .. } => this_thr_fd,
ForkArgs::Managed => RtTcb::current().thread_fd(),
};
let NewChildProc { proc_fd, thr_fd, pid } = new_child_process(args)?;
new_proc_fd = proc_fd;
new_thr_fd = thr_fd;
new_pid = pid;
copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
copy_str(**cur_thr_fd, *new_thr_fd, "name")?;
// Copy existing files into new file table, but do not reuse the same file table (i.e. new
// parent FDs will not show up for the child).
{
cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?);
cur_filetable_fd = FdGuard::new(syscall::dup(**cur_thr_fd, b"filetable")?);
// This must be done before the address space is copied.
unsafe {
initial_rsp.write(*cur_filetable_fd);
initial_rsp.add(1).write(*new_pid_fd);
initial_rsp.add(1).write(*new_proc_fd);
initial_rsp.add(2).write(*new_thr_fd);
}
}
// CoW-duplicate address space.
{
let new_addr_space_sel_fd =
FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?);
FdGuard::new(syscall::dup(*new_thr_fd, b"current-addrspace")?);
let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?);
let cur_addr_space_fd = FdGuard::new(syscall::dup(**cur_thr_fd, b"addrspace")?);
let new_addr_space_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, b"exclusive")?);
let mut grant_desc_buf = [GrantDesc::default(); 16];
@@ -794,14 +804,14 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
// Do this after the address space is cloned, since the kernel will get a shared
// reference to the TCB and whatever pages stores the signal proc control struct.
{
let new_sighandler_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sighandler")?);
let new_sighandler_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"sighandler")?);
let _ = syscall::write(
*new_sighandler_fd,
&crate::signal::current_setsighandler_struct(),
)?;
}
}
copy_env_regs(*cur_pid_fd, *new_pid_fd)?;
copy_env_regs(**cur_thr_fd, *new_thr_fd)?;
}
// Copy the file table. We do this last to ensure that all previously used file descriptors are
// closed. The only exception -- the filetable selection fd and the current filetable fd --
@@ -810,37 +820,57 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
// TODO: Use file descriptor forwarding or something similar to avoid copying the file
// table in the kernel.
let new_filetable_fd = FdGuard::new(syscall::dup(*cur_filetable_fd, b"copy")?);
let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?);
let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"current-filetable")?);
let _ = syscall::write(
*new_filetable_sel_fd,
&usize::to_ne_bytes(*new_filetable_fd),
)?;
}
let start_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"start")?);
let start_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"start")?);
let _ = syscall::write(*start_fd, &[0])?;
Ok(new_pid)
}
pub fn new_child_process(cur_pid_fd: usize) -> Result<(FdGuard, usize)> {
// Create a new context (fields such as uid/gid will be inherited from the current context).
let fd = FdGuard::new(syscall::open(
"/scheme/thisproc/new",
O_CLOEXEC,
)?);
struct NewChildProc {
#[cfg(feature = "proc")]
let pid = todo!("child pid");
proc_fd: FdGuard,
#[cfg(not(feature = "proc"))]
let pid = 1;
Ok((fd, pid))
thr_fd: FdGuard,
pid: usize,
}
pub fn copy_str(cur_pid_fd: usize, new_pid_fd: usize, key: &str) -> Result<()> {
pub fn new_child_process(args: &ForkArgs<'_>) -> Result<NewChildProc> {
match args {
ForkArgs::Managed => {
let proc_fd = &static_proc_info().proc_fd;
let child_proc_fd = FdGuard::new(syscall::dup(**proc_fd, b"fork")?);
let only_thread_fd = FdGuard::new(syscall::dup(*child_proc_fd, b"thread-0")?);
let meta = read_proc_meta(&child_proc_fd)?;
Ok(NewChildProc {
proc_fd: child_proc_fd,
thr_fd: only_thread_fd,
pid: meta.pid as usize
})
}
#[cfg(feature = "proc")]
ForkArgs::Init { .. } => unreachable!(),
#[cfg(not(feature = "proc"))]
#[cfg(feature = "proc")]
ForkArgs::Init { this_thr_fd, auth } => {
let thr_fd = FdGuard::new(syscall::dup(*auth, b"new-context")?);
Ok(NewChildProc {
thr_fd,
pid: 0,
})
}
}
}
pub fn copy_str(cur_pid_fd: usize, new_proc_fd: usize, key: &str) -> Result<()> {
let cur_name_fd = FdGuard::new(syscall::dup(cur_pid_fd, key.as_bytes())?);
let new_name_fd = FdGuard::new(syscall::dup(new_pid_fd, key.as_bytes())?);
let new_name_fd = FdGuard::new(syscall::dup(new_proc_fd, key.as_bytes())?);
// TODO: Max path size?
let mut buf = [0_u8; 256];
@@ -852,8 +882,12 @@ pub fn copy_str(cur_pid_fd: usize, new_pid_fd: usize, key: &str) -> Result<()> {
Ok(())
}
pub unsafe fn make_init() {
STATIC_PROC_INFO.get().write(crate::StaticProcInfo { pid: 1, ppid: 1 });
pub unsafe fn make_init(this_thr_fd: FdGuard) -> (&'static FdGuard, &'static FdGuard) {
let this_thr_fd = &*(*RtTcb::current().thr_fd.get()).insert(this_thr_fd);
let proc_fd = FdGuard::new(syscall::open("/scheme/proc/init", syscall::O_CLOEXEC).expect("failed to create init"));
syscall::sendfd(*proc_fd, **this_thr_fd, 0, 0).expect("failed to assign current thread to init process");
STATIC_PROC_INFO.get().write(crate::StaticProcInfo { pid: 1, ppid: 1, proc_fd });
*DYNAMIC_PROC_INFO.lock() = crate::DynamicProcInfo {
pgid: 1,
egid: 0,
@@ -861,4 +895,5 @@ pub unsafe fn make_init() {
rgid: 0,
ruid: 0,
};
(&(*STATIC_PROC_INFO.get()).proc_fd, this_thr_fd)
}
+9
View File
@@ -26,6 +26,9 @@ pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes. */
pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/
pub const AT_EXECFN: usize = 31; /* Filename of executable. */
// TODO: Downgrade aux vectors to getauxval constants on Redox, and use a regular struct for
// passing important runtime info between exec (or posix_spawn in the future) calls.
#[cfg(target_os = "redox")]
// XXX: The name AT_CWD is already used in openat... for a completely different purpose.
pub const AT_REDOX_INITIAL_CWD_PTR: usize = 32;
@@ -46,5 +49,11 @@ pub const AT_REDOX_INITIAL_DEFAULT_SCHEME_PTR: usize = 38;
#[cfg(target_os = "redox")]
pub const AT_REDOX_INITIAL_DEFAULT_SCHEME_LEN: usize = 39;
#[cfg(target_os = "redox")]
pub const AT_REDOX_PROC_FD: usize = 39;
#[cfg(target_os = "redox")]
pub const AT_REDOX_THR_FD: usize = 39;
#[cfg(target_os = "redox")]
pub const AT_REDOX_UMASK: usize = 40;