zhuowei shao преди 7 месеца
родител
ревизия
5bcdb8040b

+ 3 - 0
crates/posix_types/src/lib.rs

@@ -2,3 +2,6 @@
 
 pub mod result;
 pub mod signal;
+
+#[cfg(target_arch = "x86_64")]
+pub mod x86_64;

+ 39 - 0
crates/posix_types/src/x86_64.rs

@@ -0,0 +1,39 @@
+#[repr(transparent)]
+#[derive(Debug, Clone, Copy)]
+pub struct UserDescriptorFlags(u32);
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct UserDescriptor {
+    pub entry: u32,
+    pub base: u32,
+    pub limit: u32,
+    pub flags: UserDescriptorFlags,
+}
+
+#[allow(dead_code)]
+impl UserDescriptorFlags {
+    pub fn is_32bit_segment(&self) -> bool {
+        self.0 & 0b1 != 0
+    }
+
+    pub fn contents(&self) -> u32 {
+        self.0 & 0b110
+    }
+
+    pub fn is_read_exec_only(&self) -> bool {
+        self.0 & 0b1000 != 0
+    }
+
+    pub fn is_limit_in_pages(&self) -> bool {
+        self.0 & 0b10000 != 0
+    }
+
+    pub fn is_present(&self) -> bool {
+        self.0 & 0b100000 == 0
+    }
+
+    pub fn is_usable(&self) -> bool {
+        self.0 & 0b1000000 != 0
+    }
+}

+ 1 - 0
src/kernel/constants.rs

@@ -25,6 +25,7 @@ pub const ENXIO: u32 = 6;
 pub const ENOEXEC: u32 = 8;
 pub const EBADF: u32 = 9;
 pub const ECHILD: u32 = 10;
+pub const EAGAIN: u32 = 11;
 pub const ENOMEM: u32 = 12;
 pub const EACCES: u32 = 13;
 pub const EFAULT: u32 = 14;

+ 68 - 34
src/kernel/syscall/procops.rs

@@ -7,9 +7,10 @@ use crate::kernel::constants::{
 };
 use crate::kernel::mem::PageBuffer;
 use crate::kernel::task::{
-    new_thread_runnable, KernelStack, ProcessBuilder, ProcessList, ProgramLoader, Signal,
-    SignalAction, SignalMask, ThreadBuilder, UserDescriptor, WaitObject, WaitType,
+    do_clone, futex_wait, futex_wake, FutexFlags, FutexOp, ProcessList, ProgramLoader, Signal,
+    SignalAction, SignalMask, WaitObject, WaitType,
 };
+use crate::kernel::task::{parse_futexop, CloneArgs};
 use crate::kernel::user::dataflow::UserString;
 use crate::kernel::user::{UserPointer, UserPointerMut};
 use crate::kernel::vfs::{self, dentry::Dentry};
@@ -21,7 +22,6 @@ use alloc::ffi::CString;
 use bitflags::bitflags;
 use eonix_hal::traits::trap::RawTrapContext;
 use eonix_mm::address::Addr as _;
-use eonix_runtime::scheduler::Scheduler;
 use eonix_runtime::task::Task;
 use eonix_sync::AsProof as _;
 use posix_types::signal::SigAction;
@@ -168,7 +168,7 @@ fn execve(exec: *const u8, argv: *const u32, envp: *const u32) -> KResult<()> {
 fn exit(status: u32) -> SyscallNoReturn {
     unsafe {
         let mut procs = Task::block_on(ProcessList::get().write());
-        procs.do_kill_process(&thread.process, WaitType::Exited(status));
+        Task::block_on(procs.do_exit(&thread, WaitType::Exited(status), false));
     }
 
     SyscallNoReturn
@@ -176,7 +176,12 @@ fn exit(status: u32) -> SyscallNoReturn {
 
 #[eonix_macros::define_syscall(0xfc)]
 fn exit_group(status: u32) -> SyscallNoReturn {
-    sys_exit(thread, status)
+    unsafe {
+        let mut procs = Task::block_on(ProcessList::get().write());
+        Task::block_on(procs.do_exit(&thread, WaitType::Exited(status), true));
+    }
+
+    SyscallNoReturn
 }
 
 #[eonix_macros::define_syscall(0x07)]
@@ -298,12 +303,8 @@ fn gettid() -> KResult<u32> {
 }
 
 #[eonix_macros::define_syscall(0xf3)]
-fn set_thread_area(desc: *mut UserDescriptor) -> KResult<()> {
-    let desc_pointer = UserPointerMut::new(desc)?;
-    let mut desc = desc_pointer.read()?;
-
-    thread.set_thread_area(&mut desc)?;
-    desc_pointer.write(desc)?;
+fn set_thread_area(arch_tls: usize) -> KResult<()> {
+    thread.set_user_tls(arch_tls)?;
 
     // SAFETY: Preemption is disabled on calling `load_thread_area32()`.
     unsafe {
@@ -316,9 +317,7 @@ fn set_thread_area(desc: *mut UserDescriptor) -> KResult<()> {
 }
 
 #[eonix_macros::define_syscall(0x102)]
-fn set_tid_address(tidptr: *mut u32) -> KResult<u32> {
-    // TODO!!!: Implement this. We don't use it for now.
-    let _tidptr = UserPointerMut::new(tidptr)?;
+fn set_tid_address(tidptr: usize) -> KResult<u32> {
     Ok(thread.tid)
 }
 
@@ -537,30 +536,65 @@ fn chmod(pathname: *const u8, mode: u32) -> KResult<()> {
 }
 
 #[eonix_macros::define_syscall(0xbe)]
-fn vfork() -> u32 {
-    sys_fork(thread)
+fn vfork() -> KResult<u32> {
+    let clone_args = CloneArgs::for_vfork();
+
+    do_clone(thread, clone_args)
 }
 
 #[eonix_macros::define_syscall(0x02)]
-fn fork() -> u32 {
-    let mut procs = Task::block_on(ProcessList::get().write());
-
-    let current_process = thread.process.clone();
-    let current_pgroup = current_process.pgroup(procs.prove()).clone();
-    let current_session = current_process.session(procs.prove()).clone();
-
-    let thread_builder = ThreadBuilder::new().fork_from(&thread);
-    let (new_thread, new_process) = ProcessBuilder::new()
-        .mm_list(Task::block_on(current_process.mm_list.new_cloned()))
-        .parent(current_process)
-        .pgroup(current_pgroup)
-        .session(current_session)
-        .thread_builder(thread_builder)
-        .build(&mut procs);
-
-    Scheduler::get().spawn::<KernelStack, _>(new_thread_runnable(new_thread));
+fn fork() -> KResult<u32> {
+    let clone_args = CloneArgs::for_fork();
+
+    do_clone(thread, clone_args)
+}
+
+// x86-32 riscv
+#[eonix_macros::define_syscall(0x78)]
+fn clone(
+    clone_flags: usize,
+    new_sp: usize,
+    parent_tidptr: usize,
+    tls: usize,
+    child_tidptr: usize,
+) -> KResult<u32> {
+    let clone_args = CloneArgs::for_clone(clone_flags, new_sp, child_tidptr, parent_tidptr, tls)?;
+
+    do_clone(thread, clone_args)
+}
+
+#[eonix_macros::define_syscall(0xf0)]
+fn futex(
+    uaddr: usize,
+    op: u32,
+    val: u32,
+    time_out: usize,
+    uaddr2: usize,
+    val3: u32,
+) -> KResult<usize> {
+    let (futex_op, futex_flag) = parse_futexop(op)?;
+
+    let pid = if futex_flag.contains(FutexFlags::FUTEX_PRIVATE) {
+        Some(thread.process.pid)
+    } else {
+        None
+    };
 
-    new_process.pid
+    match futex_op {
+        FutexOp::FUTEX_WAIT => {
+            Task::block_on(futex_wait(uaddr, pid, val as u32, None))?;
+            return Ok(0);
+        }
+        FutexOp::FUTEX_WAKE => {
+            return Task::block_on(futex_wake(uaddr, pid, val as u32));
+        }
+        FutexOp::FUTEX_REQUEUE => {
+            todo!()
+        }
+        _ => {
+            todo!()
+        }
+    }
 }
 
 #[eonix_macros::define_syscall(0x77)]

+ 6 - 2
src/kernel/task.rs

@@ -1,3 +1,5 @@
+mod clone;
+mod futex;
 mod kernel_stack;
 mod loader;
 mod process;
@@ -7,11 +9,13 @@ mod session;
 mod signal;
 mod thread;
 
+pub use clone::{do_clone, CloneArgs, CloneFlags};
+pub use futex::{futex_wait, futex_wake, parse_futexop, FutexFlags, FutexOp};
 pub use kernel_stack::KernelStack;
 pub use loader::ProgramLoader;
-pub use process::{Process, ProcessBuilder, WaitObject, WaitType};
+pub use process::{alloc_pid, Process, ProcessBuilder, WaitObject, WaitType};
 pub use process_group::ProcessGroup;
 pub use process_list::ProcessList;
 pub use session::Session;
 pub use signal::{Signal, SignalAction, SignalMask};
-pub use thread::{new_thread_runnable, Thread, ThreadBuilder, UserDescriptor};
+pub use thread::{new_thread_runnable, Thread, ThreadBuilder};

+ 151 - 0
src/kernel/task/clone.rs

@@ -0,0 +1,151 @@
+use crate::{
+    kernel::{
+        task::{
+            alloc_pid, new_thread_runnable, KernelStack, ProcessBuilder, ProcessList, Thread,
+            ThreadBuilder,
+        },
+        user::UserPointerMut,
+    },
+    KResult,
+};
+use bitflags::bitflags;
+use eonix_runtime::{scheduler::Scheduler, task::Task};
+use eonix_sync::AsProof;
+
+use crate::kernel::task::Signal;
+
+bitflags! {
+    #[derive(Debug, Default)]
+    pub struct CloneFlags: usize {
+        const CLONE_VM      = 0x00000100;       /* Set if VM shared between processes.  */
+        const CLONE_FS      = 0x00000200;       /* Set if fs info shared between processes.  */
+        const CLONE_FILES   = 0x00000400;       /* Set if open files shared between processes.  */
+        const CLONE_SIGHAND = 0x00000800;       /* Set if signal handlers shared.  */
+        const CLONE_PIDFD   = 0x00001000;       /* Set if a pidfd should be placed in parent.  */
+        const CLONE_PTRACE  = 0x00002000;       /* Set if tracing continues on the child.  */
+        const CLONE_VFORK   = 0x00004000;       /* Set if the parent wants the child to wake it up on mm_release.  */
+        const CLONE_PARENT  = 0x00008000;       /* Set if we want to have the same parent as the cloner.  */
+        const CLONE_THREAD  = 0x00010000;       /* Set to add to same thread group.  */
+        const CLONE_NEWNS   = 0x00020000;       /* Set to create new namespace.  */
+        const CLONE_SYSVSEM = 0x00040000;       /* Set to shared SVID SEM_UNDO semantics.  */
+        const CLONE_SETTLS  = 0x00080000;       /* Set TLS info.  */
+        const CLONE_PARENT_SETTID = 0x00100000; /* Store TID in userlevel buffer before MM copy.  */
+        const CLONE_CHILD_CLEARTID = 0x00200000;/* Register exit futex and memory location to clear.  */
+        const CLONE_DETACHED = 0x00400000;      /* Create clone detached.  */
+        const CLONE_UNTRACED = 0x00800000;      /* Set if the tracing process can't force CLONE_PTRACE on this clone.  */
+        const CLONE_CHILD_SETTID = 0x01000000;  /* Store TID in userlevel buffer in the child.  */
+        const CLONE_NEWCGROUP   = 0x02000000;	/* New cgroup namespace.  */
+        const CLONE_NEWUTS	= 0x04000000;	    /* New utsname group.  */
+        const CLONE_NEWIPC	= 0x08000000;	    /* New ipcs.  */
+        const CLONE_NEWUSER	= 0x10000000;	    /* New user namespace.  */
+        const CLONE_NEWPID	= 0x20000000;	    /* New pid namespace.  */
+        const CLONE_NEWNET	= 0x40000000;	    /* New network namespace.  */
+        const CLONE_IO	= 0x80000000;	        /* Clone I/O context.  */
+    }
+}
+
+#[derive(Debug)]
+pub struct CloneArgs {
+    pub flags: CloneFlags,
+    pub sp: Option<usize>,           // Stack pointer for the new thread.
+    pub exit_signal: Option<Signal>, // Signal to send to the parent on exit.
+    pub child_tid_ptr: usize,        // Pointer to child TID in user space.
+    pub parent_tid_ptr: usize,       // Pointer to parent TID in user space.
+    pub tls: usize,                  // Pointer to TLS information.
+}
+
+impl CloneArgs {
+    const MASK: usize = 0xff;
+
+    pub fn for_clone(
+        flags: usize,
+        sp: usize,
+        child_tid_ptr: usize,
+        parent_tid_ptr: usize,
+        tls: usize,
+    ) -> KResult<Self> {
+        let clone_flags = CloneFlags::from_bits_truncate(flags & !Self::MASK);
+        let exit_signal = flags & Self::MASK;
+        let exit_signal = if exit_signal != 0 {
+            Some(Signal::try_from(exit_signal as u32)?)
+        } else {
+            None
+        };
+
+        assert!(sp != 0);
+
+        let clone_args = CloneArgs {
+            flags: clone_flags,
+            sp: Some(sp),
+            child_tid_ptr,
+            parent_tid_ptr,
+            exit_signal,
+            tls,
+        };
+
+        Ok(clone_args)
+    }
+
+    pub fn for_fork() -> Self {
+        CloneArgs {
+            flags: CloneFlags::empty(),
+            sp: None,
+            child_tid_ptr: 0,
+            parent_tid_ptr: 0,
+            exit_signal: Some(Signal::SIGCHLD),
+            tls: 0,
+        }
+    }
+
+    pub fn for_vfork() -> Self {
+        CloneArgs {
+            flags: CloneFlags::CLONE_VFORK | CloneFlags::CLONE_VM,
+            sp: None,
+            child_tid_ptr: 0,
+            parent_tid_ptr: 0,
+            exit_signal: Some(Signal::SIGCHLD),
+            tls: 0,
+        }
+    }
+}
+
+pub fn do_clone(thread: &Thread, clone_args: CloneArgs) -> KResult<u32> {
+    let mut procs = Task::block_on(ProcessList::get().write());
+
+    let thread_builder = ThreadBuilder::new().clone_from(&thread, &clone_args)?;
+    let current_process = thread.process.clone();
+
+    let new_pid = alloc_pid();
+
+    let new_thread = if clone_args.flags.contains(CloneFlags::CLONE_THREAD) {
+        let new_thread = thread_builder
+            .process(current_process)
+            .tid(new_pid)
+            .build(&mut procs);
+        new_thread
+    } else {
+        let current_pgroup = current_process.pgroup(procs.prove()).clone();
+        let current_session = current_process.session(procs.prove()).clone();
+
+        let (new_thread, new_process) = ProcessBuilder::new()
+            .clone_from(current_process, &clone_args)
+            .pid(new_pid)
+            .pgroup(current_pgroup)
+            .session(current_session)
+            .thread_builder(thread_builder)
+            .build(&mut procs);
+        new_thread
+    };
+
+    if clone_args.flags.contains(CloneFlags::CLONE_PARENT_SETTID) {
+        UserPointerMut::new(clone_args.parent_tid_ptr as *mut u32)?.write(new_pid)?
+    }
+
+    if clone_args.flags.contains(CloneFlags::CLONE_SETTLS) {
+        new_thread.set_user_tls(clone_args.tls)?;
+    }
+
+    Scheduler::get().spawn::<KernelStack, _>(new_thread_runnable(new_thread));
+
+    Ok(new_pid)
+}

+ 38 - 8
src/kernel/task/process.rs

@@ -3,6 +3,7 @@ use super::{
     ProcessList, Session, Signal, Thread,
 };
 use crate::kernel::constants::{ECHILD, EINTR, EPERM, ESRCH};
+use crate::kernel::task::{CloneArgs, CloneFlags};
 use crate::{
     kernel::mem::MMList,
     prelude::*,
@@ -24,10 +25,12 @@ use pointers::BorrowedArc;
 
 pub struct ProcessBuilder {
     mm_list: Option<MMList>,
+    exit_signal: Option<Signal>,
     parent: Option<Arc<Process>>,
     thread_builder: Option<ThreadBuilder>,
     pgroup: Option<Arc<ProcessGroup>>,
     session: Option<Arc<Session>>,
+    pid: Option<u32>,
 }
 
 #[derive(Debug)]
@@ -40,6 +43,8 @@ pub struct Process {
     pub wait_list: WaitList,
     pub mm_list: MMList,
 
+    pub exit_signal: Option<Signal>,
+
     /// Parent process
     ///
     /// `parent` must be valid during the whole life of the process.
@@ -137,7 +142,9 @@ impl WaitObject {
 impl ProcessBuilder {
     pub fn new() -> Self {
         Self {
+            pid: None,
             mm_list: None,
+            exit_signal: None,
             parent: None,
             thread_builder: None,
             pgroup: None,
@@ -145,11 +152,33 @@ impl ProcessBuilder {
         }
     }
 
+    pub fn clone_from(self, process: Arc<Process>, clone_args: &CloneArgs) -> Self {
+        let mm_list = if clone_args.flags.contains(CloneFlags::CLONE_VM) {
+            Task::block_on(process.mm_list.new_shared())
+        } else {
+            Task::block_on(process.mm_list.new_cloned())
+        };
+
+        self.mm_list(mm_list)
+            .exit_signal(clone_args.exit_signal.expect("should set exit signal"))
+            .parent(process)
+    }
+
+    pub fn exit_signal(mut self, exit_signal: Signal) -> Self {
+        self.exit_signal = Some(exit_signal);
+        self
+    }
+
     pub fn mm_list(mut self, mm_list: MMList) -> Self {
         self.mm_list = Some(mm_list);
         self
     }
 
+    pub fn pid(mut self, pid: u32) -> Self {
+        self.pid = Some(pid);
+        self
+    }
+
     pub fn parent(mut self, parent: Arc<Process>) -> Self {
         self.parent = Some(parent);
         self
@@ -170,18 +199,14 @@ impl ProcessBuilder {
         self
     }
 
-    fn alloc_pid() -> u32 {
-        static NEXT_PID: AtomicU32 = AtomicU32::new(1);
-        NEXT_PID.fetch_add(1, Ordering::Relaxed)
-    }
-
     pub fn build(self, process_list: &mut ProcessList) -> (Arc<Thread>, Arc<Process>) {
         let mm_list = self.mm_list.unwrap_or_else(|| MMList::new());
 
         let process = Arc::new(Process {
-            pid: Self::alloc_pid(),
+            pid: self.pid.expect("should set pid before building"),
             wait_list: WaitList::new(),
             mm_list,
+            exit_signal: self.exit_signal,
             parent: RCUPointer::empty(),
             pgroup: RCUPointer::empty(),
             session: RCUPointer::empty(),
@@ -448,9 +473,9 @@ impl Process {
         self.parent.load()
     }
 
-    pub fn notify(&self, wait: WaitObject, procs: Proof<'_, ProcessList>) {
+    pub fn notify(&self, signal: Signal, wait: WaitObject, procs: Proof<'_, ProcessList>) {
         self.wait_list.notify(wait);
-        self.raise(Signal::SIGCHLD, procs);
+        self.raise(signal, procs);
     }
 
     pub fn notify_batch(&self) -> NotifyBatch<'_, '_, '_> {
@@ -583,3 +608,8 @@ impl Drop for NotifyBatch<'_, '_, '_> {
         }
     }
 }
+
+pub fn alloc_pid() -> u32 {
+    static NEXT_PID: AtomicU32 = AtomicU32::new(1);
+    NEXT_PID.fetch_add(1, Ordering::Relaxed)
+}

+ 46 - 2
src/kernel/task/signal.rs

@@ -6,6 +6,7 @@ use super::{ProcessList, Thread, WaitObject, WaitType};
 use crate::kernel::constants::{EFAULT, EINVAL};
 use crate::{kernel::user::UserPointer, prelude::*};
 use alloc::collections::binary_heap::BinaryHeap;
+use alloc::sync::Arc;
 use arch::FpuState;
 use core::{cmp::Reverse, task::Waker};
 use eonix_hal::traits::trap::RawTrapContext;
@@ -30,7 +31,7 @@ struct SignalListInner {
     stop_waker: Option<Waker>,
 
     // TODO!!!!!: Signal disposition should be per-process.
-    actions: SignalActionList,
+    actions: Arc<SignalActionList>,
 }
 
 pub struct SignalList {
@@ -105,7 +106,7 @@ impl SignalList {
                 pending: BinaryHeap::new(),
                 signal_waker: None,
                 stop_waker: None,
-                actions: SignalActionList::new(),
+                actions: Arc::new(SignalActionList::new()),
             }),
         }
     }
@@ -214,6 +215,7 @@ impl SignalList {
                     let thread = Thread::current();
                     if let Some(parent) = thread.process.parent.load() {
                         parent.notify(
+                            Signal::SIGCHLD,
                             WaitObject {
                                 pid: thread.process.pid,
                                 code: WaitType::Stopped(signal),
@@ -238,6 +240,7 @@ impl SignalList {
 
                     if let Some(parent) = thread.process.parent.load() {
                         parent.notify(
+                            Signal::SIGCHLD,
                             WaitObject {
                                 pid: thread.process.pid,
                                 code: WaitType::Continued,
@@ -273,3 +276,44 @@ impl SignalList {
         Ok(())
     }
 }
+
+impl SignalList {
+    pub fn new_cloned(other: &Self) -> Self {
+        let inner = other.inner.lock();
+
+        debug_assert!(
+            inner.stop_waker.is_none(),
+            "We should not have a stop waker here"
+        );
+
+        Self {
+            inner: Spin::new(SignalListInner {
+                mask: inner.mask,
+                pending: BinaryHeap::new(),
+                signal_waker: None,
+                stop_waker: None,
+                actions: SignalActionList::new_cloned(&inner.actions),
+            }),
+        }
+    }
+
+    // shared only signal actions
+    pub fn new_shared(other: &Self) -> Self {
+        let inner = other.inner.lock();
+
+        debug_assert!(
+            inner.stop_waker.is_none(),
+            "We should not have a stop waker here"
+        );
+
+        Self {
+            inner: Spin::new(SignalListInner {
+                mask: inner.mask,
+                pending: BinaryHeap::new(),
+                signal_waker: None,
+                stop_waker: None,
+                actions: SignalActionList::new_shared(&inner.actions),
+            }),
+        }
+    }
+}

+ 23 - 9
src/kernel/task/signal/signal_action.rs

@@ -7,11 +7,12 @@ use crate::{
     },
     SIGNAL_NOW,
 };
-use alloc::collections::btree_map::BTreeMap;
+use alloc::{collections::btree_map::BTreeMap, sync::Arc};
 use arch::FpuState;
 use core::num::NonZero;
 use eonix_hal::{traits::trap::RawTrapContext, trap::TrapContext};
 use eonix_mm::address::{Addr as _, AddrOps as _, VAddr};
+use eonix_sync::Spin;
 use posix_types::signal::{SigAction, TryFromSigAction};
 
 #[derive(Debug, Clone, Copy)]
@@ -27,38 +28,51 @@ pub enum SignalAction {
 
 #[derive(Debug)]
 pub struct SignalActionList {
-    actions: BTreeMap<Signal, SignalAction>,
+    actions: Spin<BTreeMap<Signal, SignalAction>>,
+}
+
+impl SignalActionList {
+    pub fn new_shared(other: &Arc<Self>) -> Arc<Self> {
+        other.clone()
+    }
+
+    pub fn new_cloned(other: &Self) -> Arc<Self> {
+        Arc::new(Self {
+            actions: Spin::new(other.actions.lock().clone()),
+        })
+    }
 }
 
 impl SignalActionList {
     pub const fn new() -> Self {
         Self {
-            actions: BTreeMap::new(),
+            actions: Spin::new(BTreeMap::new()),
         }
     }
 
-    pub fn set(&mut self, signal: Signal, action: SignalAction) {
+    pub fn set(&self, signal: Signal, action: SignalAction) {
         debug_assert!(
             !matches!(signal, SIGNAL_NOW!()),
             "SIGSTOP and SIGKILL should not be set for a handler."
         );
         match action {
-            SignalAction::Default => self.actions.remove(&signal),
-            _ => self.actions.insert(signal, action),
+            SignalAction::Default => self.actions.lock().remove(&signal),
+            _ => self.actions.lock().insert(signal, action),
         };
     }
 
     pub fn get(&self, signal: Signal) -> SignalAction {
-        match self.actions.get(&signal) {
+        match self.actions.lock().get(&signal) {
             None => SignalAction::Default,
             Some(action) => action.clone(),
         }
     }
 
-    pub fn remove_non_ignore(&mut self) {
+    pub fn remove_non_ignore(&self) {
         // Remove all custom handlers except for the ignore action.
         // Default handlers should never appear in the list so we don't consider that.
         self.actions
+            .lock()
             .retain(|_, action| matches!(action, SignalAction::Ignore));
     }
 }
@@ -114,7 +128,7 @@ impl SignalAction {
 impl Clone for SignalActionList {
     fn clone(&self) -> Self {
         Self {
-            actions: self.actions.clone(),
+            actions: Spin::new(self.actions.lock().clone()),
         }
     }
 }

+ 118 - 74
src/kernel/task/thread.rs

@@ -6,8 +6,9 @@ use crate::{
     kernel::{
         interrupt::default_irq_handler,
         syscall::{syscall_handlers, SyscallHandler},
+        task::{clone::CloneArgs, CloneFlags},
         timer::timer_interrupt,
-        user::dataflow::CheckedUserPointer,
+        user::{dataflow::CheckedUserPointer, UserPointerMut},
         vfs::{filearray::FileArray, FsContext},
     },
     prelude::*,
@@ -36,6 +37,7 @@ use eonix_mm::address::{Addr as _, VAddr};
 use eonix_runtime::run::{Contexted, Run, RunState};
 use eonix_sync::AsProofMut as _;
 use pointers::BorrowedArc;
+use posix_types::x86_64::UserDescriptor;
 
 #[eonix_percpu::define_percpu]
 static CURRENT_THREAD: Option<NonNull<Thread>> = None;
@@ -52,8 +54,9 @@ pub struct ThreadBuilder {
     files: Option<Arc<FileArray>>,
     fs_context: Option<Arc<FsContext>>,
     signal_list: Option<SignalList>,
-    tls: Option<UserTLS>,
+    tls: Option<usize>,
     set_child_tid: Option<usize>,
+    clear_child_tid: Option<usize>,
 
     trap_ctx: Option<TrapContext>,
     fpu_state: Option<FpuState>,
@@ -69,7 +72,9 @@ struct ThreadInner {
 
     /// User pointer
     /// Store child thread's tid when child thread returns to user space.
-    set_child_tid: usize,
+    set_child_tid: Option<usize>,
+
+    clear_child_tid: Option<usize>,
 }
 
 pub struct Thread {
@@ -89,46 +94,6 @@ pub struct Thread {
     inner: Spin<ThreadInner>,
 }
 
-#[repr(transparent)]
-#[derive(Debug, Clone, Copy)]
-pub struct UserDescriptorFlags(u32);
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-pub struct UserDescriptor {
-    entry: u32,
-    base: u32,
-    limit: u32,
-    flags: UserDescriptorFlags,
-}
-
-#[allow(dead_code)]
-impl UserDescriptorFlags {
-    fn is_32bit_segment(&self) -> bool {
-        self.0 & 0b1 != 0
-    }
-
-    fn contents(&self) -> u32 {
-        self.0 & 0b110
-    }
-
-    fn is_read_exec_only(&self) -> bool {
-        self.0 & 0b1000 != 0
-    }
-
-    fn is_limit_in_pages(&self) -> bool {
-        self.0 & 0b10000 != 0
-    }
-
-    fn is_present(&self) -> bool {
-        self.0 & 0b100000 == 0
-    }
-
-    fn is_usable(&self) -> bool {
-        self.0 & 0b1000000 != 0
-    }
-}
-
 impl ThreadBuilder {
     pub fn new() -> Self {
         Self {
@@ -140,6 +105,7 @@ impl ThreadBuilder {
             signal_list: None,
             tls: None,
             set_child_tid: None,
+            clear_child_tid: None,
             trap_ctx: None,
             fpu_state: None,
         }
@@ -175,13 +141,18 @@ impl ThreadBuilder {
         self
     }
 
-    pub fn tls(mut self, tls: Option<UserTLS>) -> Self {
+    pub fn tls(mut self, tls: Option<usize>) -> Self {
         self.tls = tls;
         self
     }
 
-    pub fn set_child_tid(mut self, set_child_tid: usize) -> Self {
-        self.set_child_tid = Some(set_child_tid);
+    pub fn set_child_tid(mut self, set_child_tid: Option<usize>) -> Self {
+        self.set_child_tid = set_child_tid;
+        self
+    }
+
+    pub fn clear_child_tid(mut self, clear_child_tid: Option<usize>) -> Self {
+        self.clear_child_tid = clear_child_tid;
         self
     }
 
@@ -206,23 +177,63 @@ impl ThreadBuilder {
         self
     }
 
-    /// Fork the thread from another thread.
-    ///
-    /// Sets the thread's files, fs_context, signal_list, name, tls, and set_child_tid
-    pub fn fork_from(self, thread: &Thread) -> Self {
+    /// Clone the thread from another thread.
+    pub fn clone_from(self, thread: &Thread, clone_args: &CloneArgs) -> KResult<Self> {
         let inner = thread.inner.lock();
 
         let mut trap_ctx = thread.trap_ctx.borrow().clone();
         trap_ctx.set_user_return_value(0);
 
-        self.files(FileArray::new_cloned(&thread.files))
-            .fs_context(FsContext::new_cloned(&thread.fs_context))
-            .signal_list(thread.signal_list.clone())
+        if let Some(sp) = clone_args.sp {
+            trap_ctx.set_stack_pointer(sp);
+        }
+
+        let tls = if clone_args.flags.contains(CloneFlags::CLONE_SETTLS) {
+            Some(clone_args.tls)
+        } else {
+            None
+        };
+
+        let fs_context = if clone_args.flags.contains(CloneFlags::CLONE_FS) {
+            FsContext::new_shared(&thread.fs_context)
+        } else {
+            FsContext::new_cloned(&thread.fs_context)
+        };
+
+        let files = if clone_args.flags.contains(CloneFlags::CLONE_FILES) {
+            FileArray::new_shared(&thread.files)
+        } else {
+            FileArray::new_cloned(&thread.files)
+        };
+
+        let signal_list = if clone_args.flags.contains(CloneFlags::CLONE_SIGHAND) {
+            SignalList::new_shared(&thread.signal_list)
+        } else {
+            SignalList::new_cloned(&thread.signal_list)
+        };
+
+        let set_child_tid = if clone_args.flags.contains(CloneFlags::CLONE_CHILD_SETTID) {
+            Some(clone_args.child_tid_ptr)
+        } else {
+            None
+        };
+
+        let clear_child_tid = if clone_args.flags.contains(CloneFlags::CLONE_CHILD_CLEARTID) {
+            Some(clone_args.child_tid_ptr)
+        } else {
+            None
+        };
+
+        Ok(self
+            .files(files)
+            .fs_context(fs_context)
+            .signal_list(signal_list)
             .name(inner.name.clone())
-            .tls(inner.tls.clone())
-            .set_child_tid(inner.set_child_tid)
+            .tls(tls)
+            .set_child_tid(set_child_tid)
+            .clear_child_tid(clear_child_tid)
             .trap_ctx(trap_ctx)
-            .fpu_state(thread.fpu_state.borrow().clone())
+            .fpu_state(thread.fpu_state.borrow().clone()))
     }
 
     pub fn build(self, process_list: &mut ProcessList) -> Arc<Thread> {
@@ -234,7 +245,6 @@ impl ThreadBuilder {
             .fs_context
             .unwrap_or_else(|| FsContext::global().clone());
         let signal_list = self.signal_list.unwrap_or_else(|| SignalList::new());
-        let set_child_tid = self.set_child_tid.unwrap_or(0);
         let trap_ctx = self.trap_ctx.expect("TrapContext is not set");
         let fpu_state = self.fpu_state.unwrap_or_else(FpuState::new);
 
@@ -251,8 +261,9 @@ impl ThreadBuilder {
             dead: AtomicBool::new(false),
             inner: Spin::new(ThreadInner {
                 name,
-                tls: self.tls,
-                set_child_tid,
+                tls: None,
+                set_child_tid: self.set_child_tid,
+                clear_child_tid: self.clear_child_tid,
             }),
         });
 
@@ -285,28 +296,38 @@ impl Thread {
         }
     }
 
-    pub fn set_thread_area(&self, desc: &mut UserDescriptor) -> KResult<()> {
-        let mut inner = self.inner.lock();
+    pub fn set_user_tls(&self, arch_tls: usize) -> KResult<()> {
+        #[cfg(target_arch = "x86_64")]
+        {
+            let desc = arch_tls as *mut UserDescriptor;
+
+            let desc_pointer = UserPointerMut::new(desc)?;
+            let mut desc = desc_pointer.read()?;
+
+            // Clear the TLS area if it is not present.
+            if desc.flags.is_read_exec_only() && !desc.flags.is_present() {
+                if desc.limit == 0 || desc.base == 0 {
+                    return Ok(());
+                }
+
+                let len = if desc.flags.is_limit_in_pages() {
+                    (desc.limit as usize) << 12
+                } else {
+                    desc.limit as usize
+                };
 
-        // Clear the TLS area if it is not present.
-        if desc.flags.is_read_exec_only() && !desc.flags.is_present() {
-            if desc.limit == 0 || desc.base == 0 {
+                CheckedUserPointer::new(desc.base as _, len)?.zero()?;
                 return Ok(());
             }
 
-            let len = if desc.flags.is_limit_in_pages() {
-                (desc.limit as usize) << 12
-            } else {
-                desc.limit as usize
-            };
+            let (tls, entry) =
+                UserTLS::new32(desc.base, desc.limit, desc.flags.is_limit_in_pages());
+            desc.entry = entry;
 
-            CheckedUserPointer::new(desc.base as _, len)?.zero()?;
-            return Ok(());
+            self.inner.lock().tls = Some(tls);
+            desc_pointer.write(desc)?;
         }
 
-        let (tls, entry) = UserTLS::new32(desc.base, desc.limit, desc.flags.is_limit_in_pages());
-        desc.entry = entry;
-        inner.tls = Some(tls);
         Ok(())
     }
 
@@ -318,6 +339,22 @@ impl Thread {
         self.inner.lock().name.clone()
     }
 
+    pub fn set_child_tid(&self, set_child_tid: Option<usize>) {
+        self.inner.lock().set_child_tid = set_child_tid;
+    }
+
+    pub fn clear_child_tid(&self, clear_child_tid: Option<usize>) {
+        self.inner.lock().clear_child_tid = clear_child_tid;
+    }
+
+    pub fn get_set_ctid(&self) -> Option<usize> {
+        self.inner.lock().set_child_tid
+    }
+
+    pub fn get_clear_ctid(&self) -> Option<usize> {
+        self.inner.lock().clear_child_tid
+    }
+
     pub fn handle_syscall(&self, no: usize, args: [usize; 6]) -> Option<usize> {
         match syscall_handlers().get(no) {
             Some(Some(SyscallHandler {
@@ -345,6 +382,13 @@ impl Thread {
     }
 
     async fn real_run(&self) {
+        if let Some(set_ctid) = self.get_set_ctid() {
+            UserPointerMut::new(set_ctid as *mut u32)
+                .expect("set_child_tid pointer is invalid")
+                .write(self.tid)
+                .expect("set_child_tid write failed");
+        }
+
         while !self.is_dead() {
             if self.signal_list.has_pending_signal() {
                 self.signal_list

+ 3 - 0
src/lib.rs

@@ -45,6 +45,8 @@ use kernel_init::setup_memory;
 use path::Path;
 use prelude::*;
 
+use crate::kernel::task::alloc_pid;
+
 #[panic_handler]
 fn panic(info: &core::panic::PanicInfo) -> ! {
     if let Some(location) = info.location() {
@@ -174,6 +176,7 @@ async fn init_process(early_kstack: PRange) {
 
     let mut process_list = Task::block_on(ProcessList::get().write());
     let (thread, process) = ProcessBuilder::new()
+        .pid(alloc_pid())
         .mm_list(load_info.mm_list)
         .thread_builder(thread_builder)
         .build(&mut process_list);