From b85142221446b6d192d552fe74b280ba077f3a99 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 21:28:48 +0100 Subject: [PATCH] WIP: use proc manager in non-init fork. --- redox-rt/src/arch/x86_64.rs | 31 +++++---- redox-rt/src/lib.rs | 70 ++++++++++++++----- redox-rt/src/proc.rs | 129 +++++++++++++++++++++++------------- src/platform/auxv_defs.rs | 9 +++ 4 files changed, 164 insertions(+), 75 deletions(-) diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index cf9d69388a..d70f27eb88 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -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 diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs index 672e81fa01..68b56afceb 100644 --- a/redox-rt/src/lib.rs +++ b/redox-rt/src/lib.rs @@ -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 { + let mut bytes = [0_u8; size_of::()]; + let _ = syscall::read(**proc, &mut bytes)?; + Ok(*plain::from_bytes::(&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 = SyncUnsafeCell::new(StaticProcInfo { pid: 0, ppid: 0, + proc_fd: FdGuard::new(usize::MAX), }); static DYNAMIC_PROC_INFO: Mutex = Mutex::new(DynamicProcInfo { pgid: u32::MAX, @@ -218,13 +247,24 @@ static DYNAMIC_PROC_INFO: Mutex = 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)); } diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs index ee9e2666b1..c46f8d82cc 100644 --- a/redox-rt/src/proc.rs +++ b/redox-rt/src/proc.rs @@ -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( +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( total_args_envs_size: usize, extrainfo: &ExtraInfo, mut interp_override: Option, -) -> Result +) -> Result> 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 { +pub fn fork_impl(args: &ForkArgs<'_>) -> Result { 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 { Ok(pid) } -pub fn fork_inner(initial_rsp: *mut usize) -> Result { - 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 { + 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 { // 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 { // 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 { + 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) } diff --git a/src/platform/auxv_defs.rs b/src/platform/auxv_defs.rs index ae9a6d36a1..7b817dc517 100644 --- a/src/platform/auxv_defs.rs +++ b/src/platform/auxv_defs.rs @@ -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;