소스 검색

partial work: make all syscalls use the new interface

greatbridf 8 달 전
부모
커밋
33d30d3413

+ 5 - 0
arch/src/x86_64/fpu.rs

@@ -1,10 +1,15 @@
 use core::arch::asm;
 use eonix_hal_traits::fpu::RawFpuState;
 
+#[derive(Clone, Copy)]
 #[repr(align(16))]
 pub struct FpuState([u8; 512]);
 
 impl RawFpuState for FpuState {
+    fn new() -> Self {
+        Self([0; 512])
+    }
+
     fn save(&mut self) {
         unsafe {
             asm!(

+ 6 - 1
arch/src/x86_64/trap.rs

@@ -3,7 +3,7 @@ use eonix_hal_traits::{
     trap::{RawTrapContext, TrapType},
 };
 
-#[derive(Default)]
+#[derive(Clone, Copy, Default)]
 #[repr(C, align(16))]
 pub struct TrapContext {
     rax: u64,
@@ -119,6 +119,11 @@ impl RawTrapContext for TrapContext {
         self.cs & 3 == 3
     }
 
+    fn set_user_mode(&mut self, user: bool) {
+        self.cs = if user { 0x2b } else { 0x08 };
+        self.ss = if user { 0x33 } else { 0x10 };
+    }
+
     fn set_user_return_value(&mut self, retval: usize) {
         self.rax = retval as u64;
     }

+ 2 - 1
crates/eonix_hal/eonix_hal_traits/src/fpu.rs

@@ -1,5 +1,6 @@
 #[doc(notable_trait)]
-pub trait RawFpuState {
+pub trait RawFpuState: Copy {
+    fn new() -> Self;
     fn save(&mut self);
     fn restore(&mut self);
 }

+ 3 - 1
crates/eonix_hal/eonix_hal_traits/src/trap.rs

@@ -6,7 +6,7 @@ use core::marker::PhantomData;
 /// This should be implemented by the architecture-specific trap context
 /// and will be used in the HAL crates.
 #[doc(notable_trait)]
-pub trait RawTrapContext {
+pub trait RawTrapContext: Copy {
     fn new() -> Self;
 
     fn trap_type(&self) -> TrapType;
@@ -21,6 +21,8 @@ pub trait RawTrapContext {
     fn set_interrupt_enabled(&mut self, enabled: bool);
 
     fn is_user_mode(&self) -> bool;
+    fn set_user_mode(&mut self, user: bool);
+
     fn set_user_return_value(&mut self, retval: usize);
 }
 

+ 6 - 6
crates/eonix_hal/src/trap/trap_context.rs

@@ -318,13 +318,14 @@ unsafe extern "C" fn captured_trap_handler() {
     unsafe {
         naked_asm!(
             "mov ${from_context}, %rdi",
-            "mov ${to_context}, %rsi",
+            "mov %gs:0, %rsi",
+            "add ${to_context}, %rsi",
             "",
             "mov %rdi, %rsp", // We need a temporary stack to use `switch()`.
             "",
             "jmp {switch}",
             from_context = sym DIRTY_TRAP_CONTEXT,
-            to_context = sym CAPTURER_CONTEXT,
+            to_context = sym _percpu_inner_CAPTURER_CONTEXT,
             switch = sym arch::TaskContext::switch,
             options(att_syntax),
         );
@@ -335,8 +336,6 @@ unsafe extern "C" fn captured_trap_handler() {
 unsafe extern "C" fn captured_trap_return(trap_context: usize) -> ! {
     unsafe {
         naked_asm!(
-            "mov %rdi, %rsp",
-            "",
             "jmp {trap_return}",
             trap_return = sym _raw_trap_return,
             options(att_syntax),
@@ -347,11 +346,12 @@ unsafe extern "C" fn captured_trap_return(trap_context: usize) -> ! {
 impl TrapContextExt for TrapContext {
     unsafe fn trap_return(&mut self) {
         let irq_states = arch::disable_irqs_save();
-        let old_handler = TRAP_HANDLER.swap(&captured_trap_handler as *const _ as usize);
+        let old_handler = TRAP_HANDLER.swap(captured_trap_handler as *const () as usize);
 
         let mut to_ctx = arch::TaskContext::new();
+        to_ctx.ip(captured_trap_return as _);
+        to_ctx.sp(&raw mut *self as usize);
         to_ctx.interrupt(false);
-        to_ctx.call1(captured_trap_return, [&raw mut *self as usize]);
 
         unsafe {
             arch::TaskContext::switch(CAPTURER_CONTEXT.as_mut(), &mut to_ctx);

+ 3 - 6
macros/src/lib.rs

@@ -75,9 +75,8 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
 
         fn #helper_fn (
             thd: &crate::kernel::task::Thread,
-            trap_ctx: &mut arch::TrapContext,
             args: [usize; 6]
-        ) -> usize {
+        ) -> Option<usize> {
             use crate::kernel::syscall::{FromSyscallArg, SyscallRetVal};
 
             #(#args_mapped)*
@@ -90,7 +89,7 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
             //     crate::kernel::syscall::format_expand!($($arg, $arg),*),
             // );
 
-            let retval = #real_fn(thd, trap_ctx, #(#args_call),*).into_retval();
+            let retval = #real_fn(thd, #(#args_call),*).into_retval();
 
             // eonix_log::println_trace!(
             //     "trace_syscall",
@@ -106,7 +105,6 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
         #(#attrs)*
         #vis fn #real_fn(
             thread: &crate::kernel::task::Thread,
-            trap_ctx: &mut arch::TrapContext,
             #(#args),*
         ) #ty_ret #body
     }
@@ -115,9 +113,8 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
 /// Define a syscall used by the kernel. The syscall handler will be generated in the
 /// `.syscalls` section and then linked into the kernel binary.
 ///
-/// Two hidden parameters will be passed to the syscall handler:
+/// One hidden parameter will be passed to the syscall handler:
 /// - `thread: &Thread`
-/// - `trap_ctx: &mut TrapContext`
 ///
 /// The arguments of the syscall MUST implement `FromSyscallArg` trait and the return value
 /// types MUST implement `SyscallRetVal` trait.

+ 4 - 0
src/kernel.ld

@@ -95,6 +95,10 @@ SECTIONS
         KEEP(*(SORT_BY_INIT_PRIORITY(.ctors*)));
         end_ctors = .;
 
+        . = ALIGN(16);
+        SYSCALL_HANDLERS = .;
+        KEEP(*(SORT_BY_NAME(.syscalls*)));
+
         . = ALIGN(16);
         _fix_start = .;
         KEEP(*(.fix));

+ 1 - 1
src/kernel/mem.rs

@@ -8,6 +8,6 @@ mod page_alloc;
 
 pub use access::{AsMemoryBlock, KernelPageAccess, MemoryBlock, PhysAccess};
 pub(self) use mm_area::MMArea;
-pub use mm_list::{handle_page_fault, FileMapping, MMList, Mapping, Permission};
+pub use mm_list::{handle_kernel_page_fault, FileMapping, MMList, Mapping, Permission};
 pub use page_alloc::{GlobalPageAlloc, RawPage};
 pub use paging::{Page, PageBuffer};

+ 1 - 1
src/kernel/mem/mm_list.rs

@@ -24,7 +24,7 @@ use eonix_runtime::task::Task;
 use eonix_sync::{LazyLock, Mutex};
 
 pub use mapping::{FileMapping, Mapping};
-pub use page_fault::handle_page_fault;
+pub use page_fault::handle_kernel_page_fault;
 
 pub static EMPTY_PAGE: LazyLock<Page> = LazyLock::new(|| Page::zeroed());
 static KERNEL_ROOT_TABLE_PAGE: LazyLock<PageUnmanaged> = LazyLock::new(|| unsafe {

+ 106 - 88
src/kernel/mem/mm_list/page_fault.rs

@@ -1,25 +1,10 @@
 use super::{MMList, VAddr};
-use crate::kernel::mem::Mapping;
-use crate::kernel::task::{ProcessList, Signal, Thread};
-use crate::prelude::*;
-use arch::InterruptContext;
-use bitflags::bitflags;
+use crate::kernel::task::{Signal, Thread};
+use eonix_hal::traits::fault::PageFaultErrorCode;
 use eonix_mm::address::{AddrOps as _, VRange};
 use eonix_mm::paging::PAGE_SIZE;
 use eonix_runtime::task::Task;
 
-bitflags! {
-    pub struct PageFaultError: u64 {
-        const Present = 0x0001;
-        const Write = 0x0002;
-        const User = 0x0004;
-        const ReservedSet = 0x0008;
-        const InstructionFetch = 0x0010;
-        const ProtectionKey = 0x0020;
-        const SGX = 0x8000;
-    }
-}
-
 #[repr(C)]
 struct FixEntry {
     start: u64,
@@ -28,37 +13,64 @@ struct FixEntry {
     op_type: u64,
 }
 
+impl FixEntry {
+    fn start(&self) -> VAddr {
+        VAddr::from(self.start as usize)
+    }
+
+    fn end(&self) -> VAddr {
+        VAddr::from((self.start + self.length) as usize)
+    }
+
+    fn range(&self) -> VRange {
+        VRange::new(self.start(), self.end())
+    }
+
+    fn jump_address(&self) -> VAddr {
+        VAddr::from(self.jump_address as usize)
+    }
+
+    fn entries() -> &'static [FixEntry] {
+        extern "C" {
+            static FIX_START: *const FixEntry;
+            static FIX_END: *const FixEntry;
+        }
+
+        unsafe {
+            // SAFETY: `FIX_START` and `FIX_END` are defined in the
+            //         linker script in `.rodata` section.
+            core::slice::from_raw_parts(
+                FIX_START,
+                (FIX_END as usize - FIX_START as usize) / size_of::<FixEntry>(),
+            )
+        }
+    }
+}
+
 impl MMList {
-    fn handle_page_fault(
+    /// Handle a user page fault.
+    pub async fn handle_user_page_fault(
         &self,
-        int_stack: &mut InterruptContext,
         addr: VAddr,
-        error: PageFaultError,
+        error: PageFaultErrorCode,
     ) -> Result<(), Signal> {
+        debug_assert!(
+            error.contains(PageFaultErrorCode::UserAccess),
+            "Kernel mode page fault happened in user space."
+        );
+
         let inner = self.inner.borrow();
-        let inner = Task::block_on(inner.lock());
-
-        let area = match inner.areas.get(&VRange::from(addr)) {
-            Some(area) => area,
-            None => {
-                if error.contains(PageFaultError::User) {
-                    return Err(Signal::SIGBUS);
-                } else {
-                    try_page_fault_fix(int_stack, addr);
-                    return Ok(());
-                }
-            }
-        };
-
-        // User access permission violation, check user access permission.
-        if error.contains(PageFaultError::User | PageFaultError::Present) {
-            if error.contains(PageFaultError::Write) && !area.permission.write {
-                ProcessList::kill_current(Signal::SIGSEGV)
-            }
-
-            if error.contains(PageFaultError::InstructionFetch) && !area.permission.execute {
-                ProcessList::kill_current(Signal::SIGSEGV)
-            }
+        let inner = inner.lock().await;
+
+        let area = inner.areas.get(&VRange::from(addr)).ok_or(Signal::SIGBUS)?;
+
+        // Check user access permission.
+        if error.contains(PageFaultErrorCode::Write) && !area.permission.write {
+            Err(Signal::SIGSEGV)?
+        }
+
+        if error.contains(PageFaultErrorCode::InstructionFetch) && !area.permission.execute {
+            Err(Signal::SIGSEGV)?
         }
 
         let pte = inner
@@ -67,69 +79,75 @@ impl MMList {
             .next()
             .expect("If we can find the mapped area, we should be able to find the PTE");
 
-        let is_mapped = matches!(&area.mapping, Mapping::File(_));
-        if !is_mapped && !error.contains(PageFaultError::Present) {
-            try_page_fault_fix(int_stack, addr);
-            return Ok(());
-        }
-
         area.handle(pte, addr.floor() - area.range().start())
             .map_err(|_| Signal::SIGBUS)
     }
 }
 
-extern "C" {
-    static FIX_START: *const FixEntry;
-    static FIX_END: *const FixEntry;
-}
-
 /// Try to fix the page fault by jumping to the `error` address.
 ///
-/// Panic if we can't find the `ip` in the fix list.
-fn try_page_fault_fix(int_stack: &mut InterruptContext, addr: VAddr) {
-    let ip = int_stack.rip as u64;
+/// # Return
+/// Returns the new program counter after fixing.
+///
+/// # Panic
+/// Panics if we can't find the instruction causing the fault in the fix list.
+fn try_page_fault_fix(pc: VAddr, addr: VAddr) -> VAddr {
     // TODO: Use `op_type` to fix.
-
-    // SAFETY: `FIX_START` and `FIX_END` are defined in the linker script in `.rodata` section.
-    let entries = unsafe {
-        core::slice::from_raw_parts(
-            FIX_START,
-            (FIX_END as usize - FIX_START as usize) / size_of::<FixEntry>(),
-        )
-    };
-
-    for entry in entries.iter() {
-        if ip >= entry.start && ip < entry.start + entry.length {
-            int_stack.rip = entry.jump_address as u64;
-            return;
+    for entry in FixEntry::entries().iter() {
+        if pc >= entry.start() && pc < entry.end() {
+            return entry.jump_address();
         }
     }
 
-    kernel_page_fault_die(addr, ip as usize)
+    kernel_page_fault_die(addr, pc)
 }
 
-fn kernel_page_fault_die(vaddr: VAddr, ip: usize) -> ! {
+#[cold]
+fn kernel_page_fault_die(vaddr: VAddr, pc: VAddr) -> ! {
     panic!(
-        "Invalid kernel mode memory access to {:?} while executing the instruction at {:#8x}",
-        vaddr, ip
+        "Invalid kernel mode memory access to {:?} while executing the instruction at {:?}",
+        vaddr, pc
     )
 }
 
-pub fn handle_page_fault(int_stack: &mut InterruptContext) {
-    let error = PageFaultError::from_bits_truncate(int_stack.error_code);
-    let vaddr = arch::get_page_fault_address();
+pub fn handle_kernel_page_fault(
+    fault_pc: VAddr,
+    addr: VAddr,
+    error: PageFaultErrorCode,
+) -> Option<VAddr> {
+    debug_assert!(
+        !error.contains(PageFaultErrorCode::UserAccess),
+        "User mode page fault happened in kernel space."
+    );
+
+    debug_assert!(
+        !error.contains(PageFaultErrorCode::InstructionFetch),
+        "Kernel mode instruction fetch fault."
+    );
+
+    // TODO: Move this to `UserBuffer` handler since we shouldn'e get any page fault
+    //       in the kernel except for the instructions in the fix list.
+
+    let mms = &Thread::current().process.mm_list;
+    let inner = mms.inner.borrow();
+    let inner = Task::block_on(inner.lock());
+
+    let area = match inner.areas.get(&VRange::from(addr)) {
+        Some(area) => area,
+        None => {
+            return Some(try_page_fault_fix(fault_pc, addr));
+        }
+    };
 
-    let result = Thread::current()
-        .process
-        .mm_list
-        .handle_page_fault(int_stack, vaddr, error);
+    let pte = inner
+        .page_table
+        .iter_user(VRange::from(addr.floor()).grow(PAGE_SIZE))
+        .next()
+        .expect("If we can find the mapped area, we should be able to find the PTE");
 
-    if let Err(signal) = result {
-        println_debug!(
-            "Page fault on {:?} in user space at {:#x}",
-            vaddr,
-            int_stack.rip
-        );
-        ProcessList::kill_current(signal)
+    if let Err(_) = area.handle(pte, addr.floor() - area.range().start()) {
+        return Some(try_page_fault_fix(fault_pc, addr));
     }
+
+    None
 }

+ 2 - 0
src/kernel/smp.rs

@@ -18,6 +18,8 @@ define_smp_bootstrap!(4, ap_entry, {
 
 unsafe extern "C" fn ap_entry() -> ! {
     init_localcpu();
+    eonix_hal::trap::init();
+
     Scheduler::init_local_scheduler::<KernelStack>();
     println_debug!("AP{} started", local_cpu().cpuid());
 

+ 61 - 199
src/kernel/syscall.rs

@@ -1,10 +1,4 @@
-use crate::{
-    kernel::task::{ProcessList, Signal},
-    println_warn,
-};
-use arch::{ExtendedContext, InterruptContext};
-
-extern crate arch;
+use crate::kernel::task::Thread;
 
 mod file_rw;
 mod mm;
@@ -12,235 +6,103 @@ mod net;
 mod procops;
 mod sysinfo;
 
-pub(self) struct MapArgumentImpl;
-pub(self) trait MapArgument<'a, T: 'a> {
-    fn map_arg(value: u64) -> T;
-}
+const MAX_SYSCALL_NO: usize = 512;
 
-pub(self) trait MapReturnValue {
-    fn map_ret(self) -> usize;
-}
+pub struct SyscallNoReturn;
 
-impl MapReturnValue for () {
-    fn map_ret(self) -> usize {
-        0
-    }
+pub struct SyscallHandler {
+    pub handler: fn(&Thread, [usize; 6]) -> Option<usize>,
+    pub name: &'static str,
 }
 
-impl MapReturnValue for u32 {
-    fn map_ret(self) -> usize {
-        self as usize
-    }
+pub trait FromSyscallArg {
+    fn from_arg(value: usize) -> Self;
 }
 
-impl MapReturnValue for usize {
-    fn map_ret(self) -> usize {
-        self
-    }
+pub trait SyscallRetVal {
+    fn into_retval(self) -> Option<usize>;
 }
 
-impl MapArgument<'_, u64> for MapArgumentImpl {
-    fn map_arg(value: u64) -> u64 {
-        value as u64
+impl<T> SyscallRetVal for Result<T, u32>
+where
+    T: SyscallRetVal,
+{
+    fn into_retval(self) -> Option<usize> {
+        match self {
+            Ok(v) => v.into_retval(),
+            Err(e) => Some((-(e as isize)) as usize),
+        }
     }
 }
 
-impl MapArgument<'_, u32> for MapArgumentImpl {
-    fn map_arg(value: u64) -> u32 {
-        value as u32
+impl SyscallRetVal for () {
+    fn into_retval(self) -> Option<usize> {
+        Some(0)
     }
 }
 
-impl MapArgument<'_, i32> for MapArgumentImpl {
-    fn map_arg(value: u64) -> i32 {
-        value as i32
+impl SyscallRetVal for u32 {
+    fn into_retval(self) -> Option<usize> {
+        Some(self as usize)
     }
 }
 
-impl MapArgument<'_, usize> for MapArgumentImpl {
-    fn map_arg(value: u64) -> usize {
-        value as usize
+impl SyscallRetVal for usize {
+    fn into_retval(self) -> Option<usize> {
+        Some(self)
     }
 }
 
-impl<'a, T: 'a> MapArgument<'a, *const T> for MapArgumentImpl {
-    fn map_arg(value: u64) -> *const T {
-        value as *const _
+impl SyscallRetVal for SyscallNoReturn {
+    fn into_retval(self) -> Option<usize> {
+        None
     }
 }
 
-impl<'a, T: 'a> MapArgument<'a, *mut T> for MapArgumentImpl {
-    fn map_arg(value: u64) -> *mut T {
-        value as *mut _
+impl FromSyscallArg for u64 {
+    fn from_arg(value: usize) -> u64 {
+        value as u64
     }
 }
 
-macro_rules! arg_register {
-    (0, $is:ident) => {
-        $is.rbx
-    };
-    (1, $is:ident) => {
-        $is.rcx
-    };
-    (2, $is:ident) => {
-        $is.rdx
-    };
-    (3, $is:ident) => {
-        $is.rsi
-    };
-    (4, $is:ident) => {
-        $is.rdi
-    };
-    (5, $is:ident) => {
-        $is.rbp
-    };
-}
-
-#[allow(unused_macros)]
-macro_rules! format_expand {
-    ($name:ident, $arg:tt) => {
-        format_args!("{}: {:x?}", stringify!($name), $arg)
-    };
-    ($name1:ident, $arg1:tt, $($name:ident, $arg:tt),*) => {
-        format_args!("{}: {:x?}, {}", stringify!($name1), $arg1, format_expand!($($name, $arg),*))
+impl FromSyscallArg for u32 {
+    fn from_arg(value: usize) -> u32 {
+        value as u32
     }
 }
 
-macro_rules! syscall32_call {
-    ($is:ident, $handler:ident, $($arg:ident: $type:ty),*) => {{
-        use $crate::kernel::syscall::{MapArgument, MapArgumentImpl, arg_register};
-        #[allow(unused_imports)]
-        use $crate::kernel::syscall::{MapReturnValue, format_expand};
-        #[allow(unused_imports)]
-        use $crate::{kernel::task::Thread, println_trace};
-
-        $(
-            let $arg: $type =
-                MapArgumentImpl::map_arg(arg_register!(${index()}, $is));
-        )*
-
-        println_trace!(
-            "trace_syscall",
-            "tid{}: {}({}) => {{",
-            Thread::current().tid,
-            stringify!($handler),
-            format_expand!($($arg, $arg),*),
-        );
-
-        let result = $handler($($arg),*);
-
-        println_trace!(
-            "trace_syscall",
-            "tid{}: {}({}) => }} = {:x?}",
-            Thread::current().tid,
-            stringify!($handler),
-            format_expand!($($arg, $arg),*),
-            result
-        );
-
-        match result {
-            Ok(val) => MapReturnValue::map_ret(val),
-            Err(err) => (-(err as i32)) as usize,
-        }
-    }};
-}
-
-macro_rules! define_syscall32 {
-    ($name:ident, $handler:ident) => {
-        fn $name(_int_stack: &mut $crate::kernel::syscall::arch::InterruptContext,
-            _: &mut ::arch::ExtendedContext) -> usize {
-            use $crate::kernel::syscall::MapReturnValue;
-
-            match $handler() {
-                Ok(val) => MapReturnValue::map_ret(val),
-                Err(err) => (-(err as i32)) as usize,
-            }
-        }
-    };
-    ($name:ident, $handler:ident, $($arg:ident: $argt:ty),*) => {
-        fn $name(
-            int_stack: &mut $crate::kernel::syscall::arch::InterruptContext,
-            _: &mut ::arch::ExtendedContext) -> usize {
-            use $crate::kernel::syscall::syscall32_call;
-
-            syscall32_call!(int_stack, $handler, $($arg: $argt),*)
-        }
-    };
+impl FromSyscallArg for i32 {
+    fn from_arg(value: usize) -> i32 {
+        value as i32
+    }
 }
 
-macro_rules! register_syscall {
-    ($no:expr, $name:ident) => {
-        $crate::kernel::syscall::register_syscall_handler(
-            $no,
-            concat_idents!(sys_, $name),
-            stringify!($name),
-        );
-    };
+impl FromSyscallArg for usize {
+    fn from_arg(value: usize) -> usize {
+        value
+    }
 }
 
-pub(self) use {arg_register, define_syscall32, format_expand, register_syscall, syscall32_call};
-
-#[allow(dead_code)]
-pub(self) struct SyscallHandler {
-    handler: fn(&mut InterruptContext, &mut ExtendedContext) -> usize,
-    name: &'static str,
+impl<T> FromSyscallArg for *const T {
+    fn from_arg(value: usize) -> *const T {
+        value as *const T
+    }
 }
 
-pub(self) fn register_syscall_handler(
-    no: usize,
-    handler: fn(&mut InterruptContext, &mut ExtendedContext) -> usize,
-    name: &'static str,
-) {
-    // SAFETY: `SYSCALL_HANDLERS` is never modified after initialization.
-    #[allow(static_mut_refs)]
-    let syscall = unsafe { SYSCALL_HANDLERS.get_mut(no) }.unwrap();
-    assert!(
-        syscall.replace(SyscallHandler { handler, name }).is_none(),
-        "Syscall {} is already registered",
-        no
-    );
+impl<T> FromSyscallArg for *mut T {
+    fn from_arg(value: usize) -> *mut T {
+        value as *mut T
+    }
 }
 
-pub fn register_syscalls() {
-    file_rw::register();
-    procops::register();
-    mm::register();
-    net::register();
-    sysinfo::register();
-}
+pub fn syscall_handlers() -> &'static [SyscallHandler; MAX_SYSCALL_NO] {
+    extern "C" {
+        #[allow(improper_ctypes)]
+        static SYSCALL_HANDLERS: [SyscallHandler; MAX_SYSCALL_NO];
+    }
 
-const SYSCALL_HANDLERS_SIZE: usize = 404;
-static mut SYSCALL_HANDLERS: [Option<SyscallHandler>; SYSCALL_HANDLERS_SIZE] =
-    [const { None }; SYSCALL_HANDLERS_SIZE];
-
-pub fn handle_syscall32(
-    no: usize,
-    int_stack: &mut InterruptContext,
-    ext_ctx: &mut ExtendedContext,
-) {
-    // SAFETY: `SYSCALL_HANDLERS` are never modified after initialization.
-    #[allow(static_mut_refs)]
-    let syscall = unsafe { SYSCALL_HANDLERS.get(no) }.and_then(Option::as_ref);
-
-    match syscall {
-        None => {
-            println_warn!("Syscall {no}({no:#x}) isn't implemented.");
-            ProcessList::kill_current(Signal::SIGSYS);
-        }
-        Some(handler) => {
-            arch::enable_irqs();
-            let retval = (handler.handler)(int_stack, ext_ctx);
-
-            // SAFETY: `int_stack` is always valid.
-            int_stack.rax = retval as u64;
-            int_stack.r8 = 0;
-            int_stack.r9 = 0;
-            int_stack.r10 = 0;
-            int_stack.r11 = 0;
-            int_stack.r12 = 0;
-            int_stack.r13 = 0;
-            int_stack.r14 = 0;
-            int_stack.r15 = 0;
-        }
+    unsafe {
+        // SAFETY: `SYSCALL_HANDLERS` is defined in linker script.
+        &SYSCALL_HANDLERS
     }
 }

+ 91 - 142
src/kernel/syscall/file_rw.rs

@@ -1,16 +1,7 @@
-use core::mem::MaybeUninit;
-
-use bindings::{
-    statx, AT_FDCWD, AT_STATX_SYNC_AS_STAT, AT_STATX_SYNC_TYPE, AT_SYMLINK_NOFOLLOW, EBADF, EFAULT,
-    EINVAL, ENOENT, SEEK_CUR, SEEK_END, SEEK_SET, S_IFBLK, S_IFCHR,
-};
-use eonix_runtime::task::Task;
-
 use crate::{
     io::{Buffer, BufferFill},
     kernel::{
-        constants::AT_EMPTY_PATH,
-        task::Thread,
+        constants::{AT_EMPTY_PATH, EFAULT, EINVAL, ENOENT},
         user::{
             dataflow::{CheckedUserPointer, UserBuffer, UserString},
             UserPointer, UserPointerMut,
@@ -18,85 +9,88 @@ use crate::{
         vfs::{
             dentry::Dentry,
             file::{PollEvent, SeekOption},
-            filearray::FileArray,
-            FsContext,
         },
     },
     path::Path,
     prelude::*,
 };
+use bindings::{
+    statx, AT_FDCWD, AT_STATX_SYNC_AS_STAT, AT_STATX_SYNC_TYPE, AT_SYMLINK_NOFOLLOW, EBADF,
+    SEEK_CUR, SEEK_END, SEEK_SET, S_IFBLK, S_IFCHR,
+};
+use core::mem::MaybeUninit;
+use eonix_runtime::task::Task;
 
-use super::{define_syscall32, register_syscall};
-
-fn do_read(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
+#[eonix_macros::define_syscall(0x03)]
+fn read(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let mut buffer = UserBuffer::new(buffer, bufsize)?;
-    let files = FileArray::get_current();
 
-    Task::block_on(files.get(fd).ok_or(EBADF)?.read(&mut buffer))
+    Task::block_on(thread.files.get(fd).ok_or(EBADF)?.read(&mut buffer))
 }
 
-fn do_write(fd: u32, buffer: *const u8, count: usize) -> KResult<usize> {
+#[eonix_macros::define_syscall(0x04)]
+fn write(fd: u32, buffer: *const u8, count: usize) -> KResult<usize> {
     let data = unsafe { core::slice::from_raw_parts(buffer, count) };
-    let files = FileArray::get_current();
 
-    Task::block_on(files.get(fd).ok_or(EBADF)?.write(data))
+    Task::block_on(thread.files.get(fd).ok_or(EBADF)?.write(data))
 }
 
-fn do_open(path: *const u8, flags: u32, mode: u32) -> KResult<u32> {
+#[eonix_macros::define_syscall(0x05)]
+fn open(path: *const u8, flags: u32, mode: u32) -> KResult<u32> {
     let path = UserString::new(path)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let files = FileArray::get_current();
-    let context = FsContext::get_current();
-    let mode = mode & !*context.umask.lock();
+    let mode = mode & !*thread.fs_context.umask.lock();
 
-    files.open(&context, path, flags, mode)
+    thread.files.open(&thread.fs_context, path, flags, mode)
 }
 
-fn do_close(fd: u32) -> KResult<()> {
-    let files = FileArray::get_current();
-    files.close(fd)
+#[eonix_macros::define_syscall(0x06)]
+fn close(fd: u32) -> KResult<()> {
+    thread.files.close(fd)
 }
 
-fn do_dup(fd: u32) -> KResult<u32> {
-    let files = FileArray::get_current();
-    files.dup(fd)
+#[eonix_macros::define_syscall(0x29)]
+fn dup(fd: u32) -> KResult<u32> {
+    thread.files.dup(fd)
 }
 
-fn do_dup2(old_fd: u32, new_fd: u32) -> KResult<u32> {
-    let files = FileArray::get_current();
-    files.dup_to(old_fd, new_fd, 0)
+#[eonix_macros::define_syscall(0x3f)]
+fn dup2(old_fd: u32, new_fd: u32) -> KResult<u32> {
+    thread.files.dup_to(old_fd, new_fd, 0)
 }
 
-fn do_pipe2(pipe_fd: *mut [u32; 2], flags: u32) -> KResult<()> {
+#[eonix_macros::define_syscall(0x14b)]
+fn pipe2(pipe_fd: *mut [u32; 2], flags: u32) -> KResult<()> {
     let mut buffer = UserBuffer::new(pipe_fd as *mut u8, core::mem::size_of::<[u32; 2]>())?;
-    let files = FileArray::get_current();
-    let (read_fd, write_fd) = files.pipe(flags)?;
+    let (read_fd, write_fd) = thread.files.pipe(flags)?;
 
     buffer.copy(&[read_fd, write_fd])?.ok_or(EFAULT)
 }
 
-fn do_pipe(pipe_fd: *mut [u32; 2]) -> KResult<()> {
-    do_pipe2(pipe_fd, 0)
+#[eonix_macros::define_syscall(0x2a)]
+fn pipe(pipe_fd: *mut [u32; 2]) -> KResult<()> {
+    sys_pipe2(thread, pipe_fd, 0)
 }
 
-fn do_getdents(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
+#[eonix_macros::define_syscall(0x8d)]
+fn getdents(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let mut buffer = UserBuffer::new(buffer, bufsize)?;
-    let files = FileArray::get_current();
 
-    files.get(fd).ok_or(EBADF)?.getdents(&mut buffer)?;
+    thread.files.get(fd).ok_or(EBADF)?.getdents(&mut buffer)?;
     Ok(buffer.wrote())
 }
 
-fn do_getdents64(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
+#[eonix_macros::define_syscall(0xdc)]
+fn getdents64(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let mut buffer = UserBuffer::new(buffer, bufsize)?;
-    let files = FileArray::get_current();
 
-    files.get(fd).ok_or(EBADF)?.getdents64(&mut buffer)?;
+    thread.files.get(fd).ok_or(EBADF)?.getdents64(&mut buffer)?;
     Ok(buffer.wrote())
 }
 
-fn do_statx(dirfd: u32, path: *const u8, flags: u32, mask: u32, buffer: *mut u8) -> KResult<()> {
+#[eonix_macros::define_syscall(0x17f)]
+fn statx(dirfd: u32, path: *const u8, flags: u32, mask: u32, buffer: *mut u8) -> KResult<()> {
     if (flags & AT_STATX_SYNC_TYPE) != AT_STATX_SYNC_AS_STAT {
         unimplemented!("AT_STATX_SYNC_TYPE={:x}", flags & AT_STATX_SYNC_TYPE);
     }
@@ -105,7 +99,7 @@ fn do_statx(dirfd: u32, path: *const u8, flags: u32, mask: u32, buffer: *mut u8)
     let mut buffer = UserBuffer::new(buffer, core::mem::size_of::<statx>())?;
 
     if (flags & AT_EMPTY_PATH) != 0 {
-        let file = FileArray::get_current().get(dirfd).ok_or(EBADF)?;
+        let file = thread.files.get(dirfd).ok_or(EBADF)?;
         file.statx(&mut stat, mask)?;
     } else {
         let path = UserString::new(path)?;
@@ -113,16 +107,16 @@ fn do_statx(dirfd: u32, path: *const u8, flags: u32, mask: u32, buffer: *mut u8)
 
         let file;
         if dirfd != AT_FDCWD as u32 && !path.is_absolute() {
-            let at = FileArray::get_current().get(dirfd).ok_or(EBADF)?;
+            let at = thread.files.get(dirfd).ok_or(EBADF)?;
             file = Dentry::open_at(
-                &FsContext::get_current(),
+                &thread.fs_context,
                 at.as_path().ok_or(EBADF)?,
                 path,
                 (flags & AT_SYMLINK_NOFOLLOW) != AT_SYMLINK_NOFOLLOW,
             )?;
         } else {
             file = Dentry::open(
-                &FsContext::get_current(),
+                &thread.fs_context,
                 path,
                 (flags & AT_SYMLINK_NOFOLLOW) != AT_SYMLINK_NOFOLLOW,
             )?;
@@ -134,69 +128,76 @@ fn do_statx(dirfd: u32, path: *const u8, flags: u32, mask: u32, buffer: *mut u8)
     buffer.copy(&stat)?.ok_or(EFAULT)
 }
 
-fn do_mkdir(pathname: *const u8, mode: u32) -> KResult<()> {
+#[eonix_macros::define_syscall(0x27)]
+fn mkdir(pathname: *const u8, mode: u32) -> KResult<()> {
     let path = UserString::new(pathname)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let context = FsContext::get_current();
-    let mode = mode & !*context.umask.lock() & 0o777;
+    let umask = *thread.fs_context.umask.lock();
+    let mode = mode & !umask & 0o777;
 
-    let dentry = Dentry::open(&context, path, true)?;
+    let dentry = Dentry::open(&thread.fs_context, path, true)?;
 
     dentry.mkdir(mode)
 }
 
-fn do_truncate(pathname: *const u8, length: usize) -> KResult<()> {
+#[eonix_macros::define_syscall(0x5c)]
+fn truncate(pathname: *const u8, length: usize) -> KResult<()> {
     let path = UserString::new(pathname)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let dentry = Dentry::open(&FsContext::get_current(), path, true)?;
+    let dentry = Dentry::open(&thread.fs_context, path, true)?;
 
     dentry.truncate(length)
 }
 
-fn do_unlink(pathname: *const u8) -> KResult<()> {
+#[eonix_macros::define_syscall(0x0a)]
+fn unlink(pathname: *const u8) -> KResult<()> {
     let path = UserString::new(pathname)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let dentry = Dentry::open(&FsContext::get_current(), path, false)?;
+    let dentry = Dentry::open(&thread.fs_context, path, false)?;
 
     dentry.unlink()
 }
 
-fn do_symlink(target: *const u8, linkpath: *const u8) -> KResult<()> {
+#[eonix_macros::define_syscall(0x53)]
+fn symlink(target: *const u8, linkpath: *const u8) -> KResult<()> {
     let target = UserString::new(target)?;
     let linkpath = UserString::new(linkpath)?;
     let linkpath = Path::new(linkpath.as_cstr().to_bytes())?;
 
-    let dentry = Dentry::open(&FsContext::get_current(), linkpath, false)?;
+    let dentry = Dentry::open(&thread.fs_context, linkpath, false)?;
 
     dentry.symlink(target.as_cstr().to_bytes())
 }
 
-fn do_mknod(pathname: *const u8, mode: u32, dev: u32) -> KResult<()> {
+#[eonix_macros::define_syscall(0x0e)]
+fn mknod(pathname: *const u8, mode: u32, dev: u32) -> KResult<()> {
     let path = UserString::new(pathname)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let context = FsContext::get_current();
-    let mode = mode & ((!*context.umask.lock() & 0o777) | (S_IFBLK | S_IFCHR));
+    let umask = *thread.fs_context.umask.lock();
+    let mode = mode & ((!umask & 0o777) | (S_IFBLK | S_IFCHR));
 
-    let dentry = Dentry::open(&context, path, true)?;
+    let dentry = Dentry::open(&thread.fs_context, path, true)?;
 
     dentry.mknod(mode, dev)
 }
 
-fn do_readlink(pathname: *const u8, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
+#[eonix_macros::define_syscall(0x55)]
+fn readlink(pathname: *const u8, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let path = UserString::new(pathname)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let dentry = Dentry::open(&FsContext::get_current(), path, false)?;
+    let dentry = Dentry::open(&thread.fs_context, path, false)?;
 
     let mut buffer = UserBuffer::new(buffer, bufsize)?;
     dentry.readlink(&mut buffer)
 }
 
-fn do_llseek(
+#[eonix_macros::define_syscall(0x8c)]
+fn llseek(
     fd: u32,
     offset_high: u32,
     offset_low: u32,
@@ -204,8 +205,7 @@ fn do_llseek(
     whence: u32,
 ) -> KResult<()> {
     let mut result = UserBuffer::new(result as *mut u8, core::mem::size_of::<u64>())?;
-    let files = FileArray::get_current();
-    let file = files.get(fd).ok_or(EBADF)?;
+    let file = thread.files.get(fd).ok_or(EBADF)?;
 
     let offset = ((offset_high as u64) << 32) | offset_low as u64;
 
@@ -226,9 +226,9 @@ struct IoVec32 {
     len: u32,
 }
 
-fn do_readv(fd: u32, iov_user: *const IoVec32, iovcnt: u32) -> KResult<usize> {
-    let files = FileArray::get_current();
-    let file = files.get(fd).ok_or(EBADF)?;
+#[eonix_macros::define_syscall(0x91)]
+fn readv(fd: u32, iov_user: *const IoVec32, iovcnt: u32) -> KResult<usize> {
+    let file = thread.files.get(fd).ok_or(EBADF)?;
 
     let mut iov_user = UserPointer::new(iov_user as *mut IoVec32)?;
     let iov_buffers = (0..iovcnt)
@@ -258,9 +258,9 @@ fn do_readv(fd: u32, iov_user: *const IoVec32, iovcnt: u32) -> KResult<usize> {
     Ok(tot)
 }
 
-fn do_writev(fd: u32, iov_user: *const u8, iovcnt: u32) -> KResult<usize> {
-    let files = FileArray::get_current();
-    let file = files.get(fd).ok_or(EBADF)?;
+#[eonix_macros::define_syscall(0x92)]
+fn writev(fd: u32, iov_user: *const u8, iovcnt: u32) -> KResult<usize> {
+    let file = thread.files.get(fd).ok_or(EBADF)?;
 
     // TODO: Rewrite this with `UserPointer`.
     let iov_user =
@@ -295,11 +295,12 @@ fn do_writev(fd: u32, iov_user: *const u8, iovcnt: u32) -> KResult<usize> {
     Ok(tot)
 }
 
-fn do_access(pathname: *const u8, _mode: u32) -> KResult<()> {
+#[eonix_macros::define_syscall(0x21)]
+fn access(pathname: *const u8, _mode: u32) -> KResult<()> {
     let path = UserString::new(pathname)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let dentry = Dentry::open(&FsContext::get_current(), path, true)?;
+    let dentry = Dentry::open(&thread.fs_context, path, true)?;
 
     if !dentry.is_valid() {
         return Err(ENOENT);
@@ -316,10 +317,10 @@ fn do_access(pathname: *const u8, _mode: u32) -> KResult<()> {
     Ok(())
 }
 
-fn do_sendfile64(out_fd: u32, in_fd: u32, offset: *mut u8, count: usize) -> KResult<usize> {
-    let files = FileArray::get_current();
-    let in_file = files.get(in_fd).ok_or(EBADF)?;
-    let out_file = files.get(out_fd).ok_or(EBADF)?;
+#[eonix_macros::define_syscall(0xef)]
+fn sendfile64(out_fd: u32, in_fd: u32, offset: *mut u8, count: usize) -> KResult<usize> {
+    let in_file = thread.files.get(in_fd).ok_or(EBADF)?;
+    let out_file = thread.files.get(out_fd).ok_or(EBADF)?;
 
     if !offset.is_null() {
         unimplemented!("sendfile64 with offset");
@@ -328,15 +329,16 @@ fn do_sendfile64(out_fd: u32, in_fd: u32, offset: *mut u8, count: usize) -> KRes
     Task::block_on(in_file.sendfile(&out_file, count))
 }
 
-fn do_ioctl(fd: u32, request: usize, arg3: usize) -> KResult<usize> {
-    let files = FileArray::get_current();
-    let file = files.get(fd).ok_or(EBADF)?;
+#[eonix_macros::define_syscall(0x36)]
+fn ioctl(fd: u32, request: usize, arg3: usize) -> KResult<usize> {
+    let file = thread.files.get(fd).ok_or(EBADF)?;
 
     file.ioctl(request, arg3)
 }
 
-fn do_fcntl64(fd: u32, cmd: u32, arg: usize) -> KResult<usize> {
-    FileArray::get_current().fcntl(fd, cmd, arg)
+#[eonix_macros::define_syscall(0xdd)]
+fn fcntl64(fd: u32, cmd: u32, arg: usize) -> KResult<usize> {
+    thread.files.fcntl(fd, cmd, arg)
 }
 
 #[repr(C)]
@@ -347,7 +349,8 @@ struct UserPollFd {
     revents: u16,
 }
 
-fn do_poll(fds: *mut UserPollFd, nfds: u32, _timeout: u32) -> KResult<u32> {
+#[eonix_macros::define_syscall(0xa8)]
+fn poll(fds: *mut UserPollFd, nfds: u32, _timeout: u32) -> KResult<u32> {
     match nfds {
         0 => Ok(0),
         2.. => unimplemented!("Poll with {} fds", nfds),
@@ -359,7 +362,7 @@ fn do_poll(fds: *mut UserPollFd, nfds: u32, _timeout: u32) -> KResult<u32> {
             let fds = UserPointerMut::new(fds)?;
             let mut fd = fds.read()?;
 
-            let file = Thread::current().files.get(fd.fd).ok_or(EBADF)?;
+            let file = thread.files.get(fd.fd).ok_or(EBADF)?;
             fd.revents = Task::block_on(file.poll(PollEvent::from_bits_retain(fd.events)))?.bits();
 
             fds.write(fd)?;
@@ -367,57 +370,3 @@ fn do_poll(fds: *mut UserPollFd, nfds: u32, _timeout: u32) -> KResult<u32> {
         }
     }
 }
-
-define_syscall32!(sys_read, do_read, fd: u32, buffer: *mut u8, bufsize: usize);
-define_syscall32!(sys_write, do_write, fd: u32, buffer: *const u8, count: usize);
-define_syscall32!(sys_open, do_open, path: *const u8, flags: u32, mode: u32);
-define_syscall32!(sys_close, do_close, fd: u32);
-define_syscall32!(sys_dup, do_dup, fd: u32);
-define_syscall32!(sys_dup2, do_dup2, old_fd: u32, new_fd: u32);
-define_syscall32!(sys_pipe, do_pipe, pipe_fd: *mut [u32; 2]);
-define_syscall32!(sys_pipe2, do_pipe2, pipe_fd: *mut [u32; 2], flags: u32);
-define_syscall32!(sys_getdents, do_getdents, fd: u32, buffer: *mut u8, bufsize: usize);
-define_syscall32!(sys_getdents64, do_getdents64, fd: u32, buffer: *mut u8, bufsize: usize);
-define_syscall32!(sys_statx, do_statx, fd: u32, path: *const u8, flags: u32, mask: u32, buffer: *mut u8);
-define_syscall32!(sys_mkdir, do_mkdir, pathname: *const u8, mode: u32);
-define_syscall32!(sys_truncate, do_truncate, pathname: *const u8, length: usize);
-define_syscall32!(sys_unlink, do_unlink, pathname: *const u8);
-define_syscall32!(sys_symlink, do_symlink, target: *const u8, linkpath: *const u8);
-define_syscall32!(sys_readlink, do_readlink, pathname: *const u8, buffer: *mut u8, bufsize: usize);
-define_syscall32!(sys_llseek, do_llseek, fd: u32, offset_high: u32, offset_low: u32, result: *mut u64, whence: u32);
-define_syscall32!(sys_mknod, do_mknod, pathname: *const u8, mode: u32, dev: u32);
-define_syscall32!(sys_readv, do_readv, fd: u32, iov_user: *const IoVec32, iovcnt: u32);
-define_syscall32!(sys_writev, do_writev, fd: u32, iov_user: *const u8, iovcnt: u32);
-define_syscall32!(sys_access, do_access, pathname: *const u8, mode: u32);
-define_syscall32!(sys_sendfile64, do_sendfile64, out_fd: u32, in_fd: u32, offset: *mut u8, count: usize);
-define_syscall32!(sys_ioctl, do_ioctl, fd: u32, request: usize, arg3: usize);
-define_syscall32!(sys_fcntl64, do_fcntl64, fd: u32, cmd: u32, arg: usize);
-define_syscall32!(sys_poll, do_poll, fds: *mut UserPollFd, nfds: u32, timeout: u32);
-
-pub(super) fn register() {
-    register_syscall!(0x03, read);
-    register_syscall!(0x04, write);
-    register_syscall!(0x05, open);
-    register_syscall!(0x06, close);
-    register_syscall!(0x0a, unlink);
-    register_syscall!(0x0e, mknod);
-    register_syscall!(0x21, access);
-    register_syscall!(0x27, mkdir);
-    register_syscall!(0x29, dup);
-    register_syscall!(0x2a, pipe);
-    register_syscall!(0x36, ioctl);
-    register_syscall!(0x3f, dup2);
-    register_syscall!(0x53, symlink);
-    register_syscall!(0x55, readlink);
-    register_syscall!(0x5c, truncate);
-    register_syscall!(0x8c, llseek);
-    register_syscall!(0x8d, getdents);
-    register_syscall!(0x91, readv);
-    register_syscall!(0x92, writev);
-    register_syscall!(0xa8, poll);
-    register_syscall!(0xdc, getdents64);
-    register_syscall!(0xdd, fcntl64);
-    register_syscall!(0xef, sendfile64);
-    register_syscall!(0x14b, pipe2);
-    register_syscall!(0x17f, statx);
-}

+ 24 - 38
src/kernel/syscall/mm.rs

@@ -1,9 +1,8 @@
-use super::{define_syscall32, register_syscall, MapArgument, MapArgumentImpl};
+use super::FromSyscallArg;
 use crate::{
     kernel::{
         constants::{UserMmapFlags, UserMmapProtocol},
         mem::{Mapping, Permission},
-        task::Thread,
     },
     prelude::*,
 };
@@ -11,6 +10,18 @@ use bindings::{EINVAL, ENOMEM};
 use eonix_mm::address::{Addr as _, AddrOps as _, VAddr};
 use eonix_runtime::task::Task;
 
+impl FromSyscallArg for UserMmapProtocol {
+    fn from_arg(value: usize) -> UserMmapProtocol {
+        UserMmapProtocol::from_bits_truncate(value as u32)
+    }
+}
+
+impl FromSyscallArg for UserMmapFlags {
+    fn from_arg(value: usize) -> UserMmapFlags {
+        UserMmapFlags::from_bits_truncate(value as u32)
+    }
+}
+
 /// Check whether we are doing an implemented function.
 /// If `condition` is false, return `Err(err)`.
 fn check_impl(condition: bool, err: u32) -> KResult<()> {
@@ -21,7 +32,8 @@ fn check_impl(condition: bool, err: u32) -> KResult<()> {
     }
 }
 
-fn do_mmap_pgoff(
+#[eonix_macros::define_syscall(0xc0)]
+fn mmap_pgoff(
     addr: usize,
     len: usize,
     prot: UserMmapProtocol,
@@ -41,7 +53,7 @@ fn do_mmap_pgoff(
         return Err(EINVAL);
     }
 
-    let mm_list = &Thread::current().process.mm_list;
+    let mm_list = &thread.process.mm_list;
 
     // PROT_NONE, we do unmapping.
     if prot.is_empty() {
@@ -77,50 +89,24 @@ fn do_mmap_pgoff(
     addr.map(|addr| addr.addr())
 }
 
-fn do_munmap(addr: usize, len: usize) -> KResult<usize> {
+#[eonix_macros::define_syscall(0x5b)]
+fn munmap(addr: usize, len: usize) -> KResult<usize> {
     let addr = VAddr::from(addr);
     if !addr.is_page_aligned() || len == 0 {
         return Err(EINVAL);
     }
 
     let len = (len + 0xfff) & !0xfff;
-    Task::block_on(Thread::current().process.mm_list.unmap(addr, len)).map(|_| 0)
+    Task::block_on(thread.process.mm_list.unmap(addr, len)).map(|_| 0)
 }
 
-fn do_brk(addr: usize) -> KResult<usize> {
+#[eonix_macros::define_syscall(0x2d)]
+fn brk(addr: usize) -> KResult<usize> {
     let vaddr = if addr == 0 { None } else { Some(VAddr::from(addr)) };
-    Ok(Thread::current().process.mm_list.set_break(vaddr).addr())
+    Ok(thread.process.mm_list.set_break(vaddr).addr())
 }
 
-impl MapArgument<'_, UserMmapProtocol> for MapArgumentImpl {
-    fn map_arg(value: u64) -> UserMmapProtocol {
-        UserMmapProtocol::from_bits_truncate(value as u32)
-    }
-}
-
-impl MapArgument<'_, UserMmapFlags> for MapArgumentImpl {
-    fn map_arg(value: u64) -> UserMmapFlags {
-        UserMmapFlags::from_bits_truncate(value as u32)
-    }
-}
-
-#[allow(unused_variables)]
-fn do_madvise(addr: usize, len: usize, advice: u32) -> KResult<()> {
+#[eonix_macros::define_syscall(0xdb)]
+fn do_madvise(_addr: usize, _len: usize, _advice: u32) -> KResult<()> {
     Ok(())
 }
-
-define_syscall32!(sys_brk, do_brk, addr: usize);
-define_syscall32!(sys_munmap, do_munmap, addr: usize, len: usize);
-define_syscall32!(sys_madvise, do_madvise, addr: usize, len: usize, advice: u32);
-define_syscall32!(sys_mmap_pgoff, do_mmap_pgoff,
-    addr: usize, len: usize,
-    prot: UserMmapProtocol,
-    flags: UserMmapFlags,
-    fd: u32, pgoffset: usize);
-
-pub(super) fn register() {
-    register_syscall!(0x2d, brk);
-    register_syscall!(0x5b, munmap);
-    register_syscall!(0xc0, mmap_pgoff);
-    register_syscall!(0xdb, madvise);
-}

+ 3 - 11
src/kernel/syscall/net.rs

@@ -1,15 +1,7 @@
-use bindings::EINVAL;
-
+use crate::kernel::constants::EINVAL;
 use crate::prelude::*;
 
-use super::{define_syscall32, register_syscall};
-
-fn do_socket(_domain: u32, _socket_type: u32, _protocol: u32) -> KResult<u32> {
+#[eonix_macros::define_syscall(0x167)]
+fn socket(_domain: u32, _socket_type: u32, _protocol: u32) -> KResult<u32> {
     Err(EINVAL)
 }
-
-define_syscall32!(sys_socket, do_socket, domain: u32, socket_type: u32, protocol: u32);
-
-pub(super) fn register() {
-    register_syscall!(0x167, socket);
-}

+ 199 - 245
src/kernel/syscall/procops.rs

@@ -1,5 +1,5 @@
 use super::sysinfo::TimeVal;
-use super::{define_syscall32, register_syscall};
+use super::SyscallNoReturn;
 use crate::elf::ParsedElf32;
 use crate::io::Buffer;
 use crate::kernel::constants::{
@@ -7,53 +7,72 @@ use crate::kernel::constants::{
 };
 use crate::kernel::mem::PageBuffer;
 use crate::kernel::task::{
-    KernelStack, ProcessBuilder, ProcessList, Signal, SignalAction, SignalMask, Thread,
-    ThreadBuilder, ThreadRunnable, UserDescriptor, WaitObject, WaitType,
+    KernelStack, ProcessBuilder, ProcessList, Signal, SignalAction, SignalMask, ThreadBuilder,
+    ThreadRunnable, UserDescriptor, WaitObject, WaitType,
 };
 use crate::kernel::user::dataflow::UserString;
 use crate::kernel::user::{UserPointer, UserPointerMut};
-use crate::kernel::vfs::dentry::Dentry;
-use crate::kernel::vfs::{self, FsContext};
+use crate::kernel::vfs::{self, dentry::Dentry};
 use crate::path::Path;
 use crate::SIGNAL_NOW;
 use crate::{kernel::user::dataflow::UserBuffer, prelude::*};
 use alloc::borrow::ToOwned;
 use alloc::ffi::CString;
-use arch::{ExtendedContext, InterruptContext};
 use bindings::{EINVAL, ENOENT, ENOTDIR, ERANGE, ESRCH};
 use bitflags::bitflags;
-use eonix_mm::address::{Addr as _, VAddr};
+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;
 
-fn do_umask(mask: u32) -> KResult<u32> {
-    let context = FsContext::get_current();
-    let mut umask = context.umask.lock();
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+struct RLimit {
+    rlim_cur: u64,
+    rlim_max: u64,
+}
+
+bitflags! {
+    pub struct UserWaitOptions: u32 {
+        const WNOHANG = 1;
+        const WUNTRACED = 2;
+        const WCONTINUED = 8;
+    }
+}
+
+#[eonix_macros::define_syscall(0x3c)]
+fn umask(mask: u32) -> KResult<u32> {
+    let mut umask = thread.fs_context.umask.lock();
 
     let old = *umask;
     *umask = mask & 0o777;
     Ok(old)
 }
 
-fn do_getcwd(buffer: *mut u8, bufsize: usize) -> KResult<usize> {
-    let context = FsContext::get_current();
+#[eonix_macros::define_syscall(0xb7)]
+fn getcwd(buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let mut user_buffer = UserBuffer::new(buffer, bufsize)?;
     let mut buffer = PageBuffer::new();
 
-    context.cwd.lock().get_path(&context, &mut buffer)?;
+    thread
+        .fs_context
+        .cwd
+        .lock()
+        .get_path(&thread.fs_context, &mut buffer)?;
+
     user_buffer.fill(buffer.data())?.ok_or(ERANGE)?;
 
     Ok(buffer.wrote())
 }
 
-fn do_chdir(path: *const u8) -> KResult<()> {
-    let context = FsContext::get_current();
+#[eonix_macros::define_syscall(0x0c)]
+fn chdir(path: *const u8) -> KResult<()> {
     let path = UserString::new(path)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let dentry = Dentry::open(&context, path, true)?;
+    let dentry = Dentry::open(&thread.fs_context, path, true)?;
     if !dentry.is_valid() {
         return Err(ENOENT);
     }
@@ -62,17 +81,22 @@ fn do_chdir(path: *const u8) -> KResult<()> {
         return Err(ENOTDIR);
     }
 
-    *context.cwd.lock() = dentry;
+    *thread.fs_context.cwd.lock() = dentry;
     Ok(())
 }
 
-fn do_mount(source: *const u8, target: *const u8, fstype: *const u8, flags: usize) -> KResult<()> {
+#[eonix_macros::define_syscall(0x15)]
+fn mount(source: *const u8, target: *const u8, fstype: *const u8, flags: usize) -> KResult<()> {
     let source = UserString::new(source)?;
     let target = UserString::new(target)?;
     let fstype = UserString::new(fstype)?;
 
-    let context = FsContext::get_current();
-    let mountpoint = Dentry::open(&context, Path::new(target.as_cstr().to_bytes())?, true)?;
+    let mountpoint = Dentry::open(
+        &thread.fs_context,
+        Path::new(target.as_cstr().to_bytes())?,
+        true,
+    )?;
+
     if !mountpoint.is_valid() {
         return Err(ENOENT);
     }
@@ -86,106 +110,79 @@ fn do_mount(source: *const u8, target: *const u8, fstype: *const u8, flags: usiz
     )
 }
 
-/// # Return
-/// `(entry_ip, sp)`
-fn do_execve(exec: &[u8], argv: Vec<CString>, envp: Vec<CString>) -> KResult<(VAddr, VAddr)> {
-    let dentry = Dentry::open(&FsContext::get_current(), Path::new(exec)?, true)?;
-    if !dentry.is_valid() {
-        return Err(ENOENT);
-    }
+fn get_strings(mut ptr_strings: UserPointer<'_, u32>) -> KResult<Vec<CString>> {
+    let mut strings = Vec::new();
 
-    // TODO: When `execve` is called by one of the threads in a process, the other threads
-    //       should be terminated and `execve` is performed in the thread group leader.
-    let elf = ParsedElf32::parse(dentry.clone())?;
-    let result = elf.load(argv, envp);
-    if let Ok((ip, sp, mm_list)) = result {
-        unsafe {
-            // SAFETY: We are doing execve, all other threads are terminated.
-            Thread::current().process.mm_list.replace(Some(mm_list));
+    loop {
+        let addr = ptr_strings.read()?;
+        if addr == 0 {
+            break;
         }
-        Thread::current().files.on_exec();
-        Thread::current().signal_list.clear_non_ignore();
-        Thread::current().set_name(dentry.name().clone());
-
-        Ok((ip, sp))
-    } else {
-        drop(dentry);
 
-        // We can't hold any ownership when we call `kill_current`.
-        ProcessList::kill_current(Signal::SIGSEGV);
+        let user_string = UserString::new(addr as *const u8)?;
+        strings.push(user_string.as_cstr().to_owned());
+        ptr_strings = ptr_strings.offset(1)?;
     }
-}
-
-fn sys_execve(int_stack: &mut InterruptContext, _: &mut ExtendedContext) -> usize {
-    match (|| -> KResult<()> {
-        let exec = int_stack.rbx as *const u8;
-        let exec = UserString::new(exec)?;
 
-        // TODO!!!!!: copy from user
-        let mut argv = UserPointer::<u32>::new_vaddr(int_stack.rcx as _)?;
-        let mut envp = UserPointer::<u32>::new_vaddr(int_stack.rdx as _)?;
+    Ok(strings)
+}
 
-        let mut argv_vec = Vec::new();
-        let mut envp_vec = Vec::new();
+#[eonix_macros::define_syscall(0x0b)]
+fn execve(exec: *const u8, argv: *const u32, envp: *const u32) -> KResult<()> {
+    let exec = UserString::new(exec)?;
+    let argv = get_strings(UserPointer::new(argv)?)?;
+    let envp = get_strings(UserPointer::new(envp)?)?;
 
-        loop {
-            let arg = argv.read()?;
-            if arg == 0 {
-                break;
-            }
+    let dentry = Dentry::open(
+        &thread.fs_context,
+        Path::new(exec.as_cstr().to_bytes())?,
+        true,
+    )?;
 
-            let arg = UserString::new(arg as *const u8)?;
-            argv_vec.push(arg.as_cstr().to_owned());
-            argv = argv.offset(1)?;
-        }
-
-        loop {
-            let arg = envp.read()?;
-            if arg == 0 {
-                break;
-            }
+    if !dentry.is_valid() {
+        Err(ENOENT)?;
+    }
 
-            let arg = UserString::new(arg as *const u8)?;
-            envp_vec.push(arg.as_cstr().to_owned());
-            envp = envp.offset(1)?;
+    // TODO: When `execve` is called by one of the threads in a process, the other threads
+    //       should be terminated and `execve` is performed in the thread group leader.
+    let elf = ParsedElf32::parse(dentry.clone())?;
+    if let Ok((ip, sp, mm_list)) = elf.load(argv, envp) {
+        unsafe {
+            // SAFETY: We are doing execve, all other threads are terminated.
+            thread.process.mm_list.replace(Some(mm_list));
         }
+        thread.files.on_exec();
+        thread.signal_list.clear_non_ignore();
+        thread.set_name(dentry.name().clone());
 
-        let (ip, sp) = do_execve(exec.as_cstr().to_bytes(), argv_vec, envp_vec)?;
-
-        int_stack.rip = ip.addr() as u64;
-        int_stack.rsp = sp.addr() as u64;
+        let mut trap_ctx = thread.trap_ctx.borrow();
+        trap_ctx.set_program_counter(ip.addr());
+        trap_ctx.set_stack_pointer(sp.addr());
         Ok(())
-    })() {
-        Ok(_) => 0,
-        Err(err) => -(err as i32) as _,
+    } else {
+        // We can't hold any ownership when we call `kill_current`.
+        // ProcessList::kill_current(Signal::SIGSEGV);
+        todo!()
     }
 }
 
-fn sys_exit(int_stack: &mut InterruptContext, _: &mut ExtendedContext) -> usize {
-    let status = int_stack.rbx as u32;
-
+#[eonix_macros::define_syscall(0x01)]
+fn exit(status: u32) {
     unsafe {
         let mut procs = Task::block_on(ProcessList::get().write());
-        procs.do_kill_process(&Thread::current().process, WaitType::Exited(status));
+        procs.do_kill_process(&thread.process, WaitType::Exited(status));
     }
 
-    unsafe {
-        eonix_preempt::disable();
-
-        // SAFETY: Preempt count == 1.
-        Thread::exit();
-    }
+    todo!()
 }
 
-bitflags! {
-    pub struct UserWaitOptions: u32 {
-        const WNOHANG = 1;
-        const WUNTRACED = 2;
-        const WCONTINUED = 8;
-    }
+#[eonix_macros::define_syscall(0xfc)]
+fn exit_group(status: u32) {
+    sys_exit(thread, status)
 }
 
-fn do_waitpid(_waitpid: u32, arg1: *mut u32, options: u32) -> KResult<u32> {
+#[eonix_macros::define_syscall(0x07)]
+fn waitpid(_waitpid: u32, arg1: *mut u32, options: u32) -> KResult<u32> {
     // if waitpid != u32::MAX {
     //     unimplemented!("waitpid with pid {waitpid}")
     // }
@@ -194,7 +191,7 @@ fn do_waitpid(_waitpid: u32, arg1: *mut u32, options: u32) -> KResult<u32> {
         Some(options) => options,
     };
 
-    let wait_object = Task::block_on(Thread::current().process.wait(
+    let wait_object = Task::block_on(thread.process.wait(
         options.contains(UserWaitOptions::WNOHANG),
         options.contains(UserWaitOptions::WUNTRACED),
         options.contains(UserWaitOptions::WCONTINUED),
@@ -211,20 +208,23 @@ fn do_waitpid(_waitpid: u32, arg1: *mut u32, options: u32) -> KResult<u32> {
     }
 }
 
-fn do_wait4(waitpid: u32, arg1: *mut u32, options: u32, rusage: *mut ()) -> KResult<u32> {
+#[eonix_macros::define_syscall(0x72)]
+fn wait4(waitpid: u32, arg1: *mut u32, options: u32, rusage: *mut ()) -> KResult<u32> {
     if rusage.is_null() {
-        do_waitpid(waitpid, arg1, options)
+        sys_waitpid(thread, waitpid, arg1, options)
     } else {
         unimplemented!("wait4 with rusage")
     }
 }
 
-fn do_setsid() -> KResult<u32> {
-    Thread::current().process.setsid()
+#[eonix_macros::define_syscall(0x42)]
+fn setsid() -> KResult<u32> {
+    thread.process.setsid()
 }
 
-fn do_setpgid(pid: u32, pgid: i32) -> KResult<()> {
-    let pid = if pid == 0 { Thread::current().process.pid } else { pid };
+#[eonix_macros::define_syscall(0x39)]
+fn setpgid(pid: u32, pgid: i32) -> KResult<()> {
+    let pid = if pid == 0 { thread.process.pid } else { pid };
 
     let pgid = match pgid {
         0 => pid,
@@ -232,12 +232,13 @@ fn do_setpgid(pid: u32, pgid: i32) -> KResult<()> {
         _ => return Err(EINVAL),
     };
 
-    Thread::current().process.setpgid(pid, pgid)
+    thread.process.setpgid(pid, pgid)
 }
 
-fn do_getsid(pid: u32) -> KResult<u32> {
+#[eonix_macros::define_syscall(0x93)]
+fn getsid(pid: u32) -> KResult<u32> {
     if pid == 0 {
-        Ok(Thread::current().process.session_rcu().sid)
+        Ok(thread.process.session_rcu().sid)
     } else {
         let procs = Task::block_on(ProcessList::get().read());
         procs
@@ -247,9 +248,10 @@ fn do_getsid(pid: u32) -> KResult<u32> {
     }
 }
 
-fn do_getpgid(pid: u32) -> KResult<u32> {
+#[eonix_macros::define_syscall(0x84)]
+fn getpgid(pid: u32) -> KResult<u32> {
     if pid == 0 {
-        Ok(Thread::current().process.pgroup_rcu().pgid)
+        Ok(thread.process.pgroup_rcu().pgid)
     } else {
         let procs = Task::block_on(ProcessList::get().read());
         procs
@@ -259,66 +261,80 @@ fn do_getpgid(pid: u32) -> KResult<u32> {
     }
 }
 
-fn do_getpid() -> KResult<u32> {
-    Ok(Thread::current().process.pid)
+#[eonix_macros::define_syscall(0x14)]
+fn getpid() -> KResult<u32> {
+    Ok(thread.process.pid)
 }
 
-fn do_getppid() -> KResult<u32> {
-    Ok(Thread::current().process.parent_rcu().map_or(0, |x| x.pid))
+#[eonix_macros::define_syscall(0x40)]
+fn getppid() -> KResult<u32> {
+    Ok(thread.process.parent_rcu().map_or(0, |x| x.pid))
 }
 
-fn do_getuid() -> KResult<u32> {
+#[eonix_macros::define_syscall(0xc7)]
+fn getuid() -> KResult<u32> {
     // All users are root for now.
     Ok(0)
 }
 
-fn do_geteuid() -> KResult<u32> {
+#[eonix_macros::define_syscall(0xca)]
+fn geteuid() -> KResult<u32> {
     // All users are root for now.
     Ok(0)
 }
 
-fn do_getgid() -> KResult<u32> {
+#[eonix_macros::define_syscall(0x2f)]
+fn getgid() -> KResult<u32> {
     // All users are root for now.
     Ok(0)
 }
 
-fn do_gettid() -> KResult<u32> {
-    Ok(Thread::current().tid)
+#[eonix_macros::define_syscall(0xc8)]
+fn getgid32() -> KResult<u32> {
+    sys_getgid(thread)
 }
 
-fn do_set_thread_area(desc: *mut UserDescriptor) -> KResult<()> {
+#[eonix_macros::define_syscall(0xe0)]
+fn gettid() -> KResult<u32> {
+    Ok(thread.tid)
+}
+
+#[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::current().set_thread_area(&mut desc)?;
+    thread.set_thread_area(&mut desc)?;
     desc_pointer.write(desc)?;
 
     // SAFETY: Preemption is disabled on calling `load_thread_area32()`.
     unsafe {
         eonix_preempt::disable();
-        Thread::current().load_thread_area32();
+        thread.load_thread_area32();
         eonix_preempt::enable();
     }
 
     Ok(())
 }
 
-fn do_set_tid_address(tidptr: *mut u32) -> KResult<u32> {
+#[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)?;
-    Ok(Thread::current().tid)
+    Ok(thread.tid)
 }
 
-fn do_prctl(option: u32, arg2: usize) -> KResult<()> {
+#[eonix_macros::define_syscall(0xac)]
+fn prctl(option: u32, arg2: usize) -> KResult<()> {
     match option {
         PR_SET_NAME => {
             let name = UserPointer::new(arg2 as *mut [u8; 16])?.read()?;
             let len = name.iter().position(|&c| c == 0).unwrap_or(15);
-            Thread::current().set_name(name[..len].into());
+            thread.set_name(name[..len].into());
             Ok(())
         }
         PR_GET_NAME => {
-            let name = Thread::current().get_name();
+            let name = thread.get_name();
             let len = name.len().min(15);
             let name: [u8; 16] = core::array::from_fn(|i| if i < len { name[i] } else { 0 });
             UserPointerMut::new(arg2 as *mut [u8; 16])?.write(name)?;
@@ -328,14 +344,15 @@ fn do_prctl(option: u32, arg2: usize) -> KResult<()> {
     }
 }
 
-fn do_kill(pid: i32, sig: u32) -> KResult<()> {
+#[eonix_macros::define_syscall(0x25)]
+fn kill(pid: i32, sig: u32) -> KResult<()> {
     let procs = Task::block_on(ProcessList::get().read());
     match pid {
         // Send signal to every process for which the calling process has
         // permission to send signals.
         -1 => unimplemented!("kill with pid -1"),
         // Send signal to every process in the process group.
-        0 => Thread::current()
+        0 => thread
             .process
             .pgroup(procs.prove())
             .raise(Signal::try_from(sig)?, procs.prove()),
@@ -354,7 +371,8 @@ fn do_kill(pid: i32, sig: u32) -> KResult<()> {
     Ok(())
 }
 
-fn do_tkill(tid: u32, sig: u32) -> KResult<()> {
+#[eonix_macros::define_syscall(0xee)]
+fn tkill(tid: u32, sig: u32) -> KResult<()> {
     Task::block_on(ProcessList::get().read())
         .try_find_thread(tid)
         .ok_or(ESRCH)?
@@ -362,12 +380,13 @@ fn do_tkill(tid: u32, sig: u32) -> KResult<()> {
     Ok(())
 }
 
-fn do_rt_sigprocmask(how: u32, set: *mut u64, oldset: *mut u64, sigsetsize: usize) -> KResult<()> {
+#[eonix_macros::define_syscall(0xaf)]
+fn rt_sigprocmask(how: u32, set: *mut u64, oldset: *mut u64, sigsetsize: usize) -> KResult<()> {
     if sigsetsize != size_of::<u64>() {
         return Err(EINVAL);
     }
 
-    let old_mask = u64::from(Thread::current().signal_list.get_mask());
+    let old_mask = u64::from(thread.signal_list.get_mask());
     if !oldset.is_null() {
         UserPointerMut::new(oldset)?.write(old_mask)?;
     }
@@ -379,16 +398,17 @@ fn do_rt_sigprocmask(how: u32, set: *mut u64, oldset: *mut u64, sigsetsize: usiz
     };
 
     match how {
-        SIG_BLOCK => Thread::current().signal_list.mask(new_mask),
-        SIG_UNBLOCK => Thread::current().signal_list.unmask(new_mask),
-        SIG_SETMASK => Thread::current().signal_list.set_mask(new_mask),
+        SIG_BLOCK => thread.signal_list.mask(new_mask),
+        SIG_UNBLOCK => thread.signal_list.unmask(new_mask),
+        SIG_SETMASK => thread.signal_list.set_mask(new_mask),
         _ => return Err(EINVAL),
     }
 
     Ok(())
 }
 
-fn do_rt_sigaction(
+#[eonix_macros::define_syscall(0xae)]
+fn rt_sigaction(
     signum: u32,
     act: *const SigAction,
     oldact: *mut SigAction,
@@ -404,7 +424,7 @@ fn do_rt_sigaction(
         return Err(EINVAL);
     }
 
-    let old_action = Thread::current().signal_list.get_action(signal);
+    let old_action = thread.signal_list.get_action(signal);
     if !oldact.is_null() {
         UserPointerMut::new(oldact)?.write(old_action.into())?;
     }
@@ -413,20 +433,14 @@ fn do_rt_sigaction(
         let new_action = UserPointer::new(act)?.read()?;
         let action: SignalAction = new_action.try_into()?;
 
-        Thread::current().signal_list.set_action(signal, action)?;
+        thread.signal_list.set_action(signal, action)?;
     }
 
     Ok(())
 }
 
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-struct RLimit {
-    rlim_cur: u64,
-    rlim_max: u64,
-}
-
-fn do_prlimit64(
+#[eonix_macros::define_syscall(0x154)]
+fn prlimit64(
     pid: u32,
     resource: u32,
     new_limit: *const RLimit,
@@ -456,8 +470,9 @@ fn do_prlimit64(
     }
 }
 
-fn do_getrlimit(resource: u32, rlimit: *mut RLimit) -> KResult<()> {
-    do_prlimit64(0, resource, core::ptr::null(), rlimit)
+#[eonix_macros::define_syscall(0xbf)]
+fn getrlimit(resource: u32, rlimit: *mut RLimit) -> KResult<()> {
+    sys_prlimit64(thread, 0, resource, core::ptr::null(), rlimit)
 }
 
 #[repr(C)]
@@ -481,7 +496,8 @@ struct RUsage {
     ru_nivcsw: u32,
 }
 
-fn do_getrusage(who: u32, rusage: *mut RUsage) -> KResult<()> {
+#[eonix_macros::define_syscall(0x4d)]
+fn getrusage(who: u32, rusage: *mut RUsage) -> KResult<()> {
     if who != 0 {
         return Err(ENOSYS);
     }
@@ -509,12 +525,12 @@ fn do_getrusage(who: u32, rusage: *mut RUsage) -> KResult<()> {
     Ok(())
 }
 
-fn do_chmod(pathname: *const u8, mode: u32) -> KResult<()> {
-    let context = FsContext::get_current();
+#[eonix_macros::define_syscall(0x0f)]
+fn chmod(pathname: *const u8, mode: u32) -> KResult<()> {
     let path = UserString::new(pathname)?;
     let path = Path::new(path.as_cstr().to_bytes())?;
 
-    let dentry = Dentry::open(&context, path, true)?;
+    let dentry = Dentry::open(&thread.fs_context, path, true)?;
     if !dentry.is_valid() {
         return Err(ENOENT);
     }
@@ -522,55 +538,20 @@ fn do_chmod(pathname: *const u8, mode: u32) -> KResult<()> {
     dentry.chmod(mode)
 }
 
-define_syscall32!(sys_chdir, do_chdir, path: *const u8);
-define_syscall32!(sys_umask, do_umask, mask: u32);
-define_syscall32!(sys_getcwd, do_getcwd, buffer: *mut u8, bufsize: usize);
-define_syscall32!(sys_waitpid, do_waitpid, waitpid: u32, arg1: *mut u32, options: u32);
-define_syscall32!(sys_wait4, do_wait4, waitpid: u32, arg1: *mut u32, options: u32, rusage: *mut ());
-define_syscall32!(sys_setsid, do_setsid);
-define_syscall32!(sys_setpgid, do_setpgid, pid: u32, pgid: i32);
-define_syscall32!(sys_getsid, do_getsid, pid: u32);
-define_syscall32!(sys_getpgid, do_getpgid, pid: u32);
-define_syscall32!(sys_getpid, do_getpid);
-define_syscall32!(sys_getppid, do_getppid);
-define_syscall32!(sys_getuid, do_getuid);
-define_syscall32!(sys_geteuid, do_geteuid);
-define_syscall32!(sys_getgid, do_getgid);
-define_syscall32!(sys_gettid, do_gettid);
-define_syscall32!(sys_mount, do_mount,
-    source: *const u8, target: *const u8,fstype: *const u8, flags: usize);
-define_syscall32!(sys_set_thread_area, do_set_thread_area, desc: *mut UserDescriptor);
-define_syscall32!(sys_set_tid_address, do_set_tid_address, tidptr: *mut u32);
-define_syscall32!(sys_prctl, do_prctl, option: u32, arg2: usize);
-define_syscall32!(sys_arch_prctl, do_prctl, option: u32, arg2: usize);
-define_syscall32!(sys_kill, do_kill, pid: i32, sig: u32);
-define_syscall32!(sys_tkill, do_tkill, tid: u32, sig: u32);
-define_syscall32!(sys_rt_sigprocmask, do_rt_sigprocmask,
-    how: u32, set: *mut u64, oldset: *mut u64, sigsetsize: usize);
-define_syscall32!(sys_rt_sigaction, do_rt_sigaction,
-    signum: u32, act: *const SigAction, oldact: *mut SigAction, sigsetsize: usize);
-define_syscall32!(sys_prlimit64, do_prlimit64,
-    pid: u32, resource: u32, new_limit: *const RLimit, old_limit: *mut RLimit);
-define_syscall32!(sys_getrlimit, do_getrlimit, resource: u32, rlimit: *mut RLimit);
-define_syscall32!(sys_getrusage, do_getrusage, who: u32, rlimit: *mut RUsage);
-define_syscall32!(sys_chmod, do_chmod, pathname: *const u8, mode: u32);
-
-fn sys_vfork(int_stack: &mut InterruptContext, ext: &mut ExtendedContext) -> usize {
-    sys_fork(int_stack, ext)
-}
-
-fn sys_fork(int_stack: &mut InterruptContext, _: &mut ExtendedContext) -> usize {
+#[eonix_macros::define_syscall(0xbe)]
+fn vfork() -> u32 {
+    sys_fork(thread)
+}
+
+#[eonix_macros::define_syscall(0x02)]
+fn fork() -> u32 {
     let mut procs = Task::block_on(ProcessList::get().write());
 
-    let current = Thread::current();
-    let current_process = current.process.clone();
+    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 mut new_int_context = int_stack.clone();
-    new_int_context.set_return_value(0);
-
-    let thread_builder = ThreadBuilder::new().fork_from(&current);
+    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)
@@ -579,59 +560,32 @@ fn sys_fork(int_stack: &mut InterruptContext, _: &mut ExtendedContext) -> usize
         .thread_builder(thread_builder)
         .build(&mut procs);
 
-    Scheduler::get()
-        .spawn::<KernelStack, _>(ThreadRunnable::from_context(new_thread, new_int_context));
+    Scheduler::get().spawn::<KernelStack, _>(ThreadRunnable::new(new_thread));
 
-    new_process.pid as usize
+    new_process.pid
 }
 
-fn sys_sigreturn(int_stack: &mut InterruptContext, ext_ctx: &mut ExtendedContext) -> usize {
-    let result = Thread::current().signal_list.restore(int_stack, ext_ctx);
-    match result {
-        Ok(ret) => ret,
-        Err(_) => {
-            println_warn!("`sigreturn` failed in thread {}!", Thread::current().tid);
-            Thread::current().raise(Signal::SIGSEGV);
-            0
-        }
-    }
+#[eonix_macros::define_syscall(0x77)]
+fn sigreturn() -> KResult<SyscallNoReturn> {
+    thread
+        .signal_list
+        .restore(
+            &mut thread.trap_ctx.borrow(),
+            &mut thread.fpu_state.borrow(),
+        )
+        .inspect_err(|err| {
+            println_warn!(
+                "`sigreturn` failed in thread {} with error {err}!",
+                thread.tid
+            );
+            thread.raise(Signal::SIGSEGV);
+        })?;
+
+    Ok(SyscallNoReturn)
 }
 
-pub(super) fn register() {
-    register_syscall!(0x01, exit);
-    register_syscall!(0x02, fork);
-    register_syscall!(0x07, waitpid);
-    register_syscall!(0x0b, execve);
-    register_syscall!(0x0c, chdir);
-    register_syscall!(0x0f, chmod);
-    register_syscall!(0x14, getpid);
-    register_syscall!(0x15, mount);
-    register_syscall!(0x25, kill);
-    register_syscall!(0x2f, getgid);
-    register_syscall!(0x39, setpgid);
-    register_syscall!(0x3c, umask);
-    register_syscall!(0x40, getppid);
-    register_syscall!(0x42, setsid);
-    register_syscall!(0x4d, getrusage);
-    register_syscall!(0x72, wait4);
-    register_syscall!(0x77, sigreturn);
-    register_syscall!(0x84, getpgid);
-    register_syscall!(0x93, getsid);
-    register_syscall!(0xac, prctl);
-    register_syscall!(0xae, rt_sigaction);
-    register_syscall!(0xaf, rt_sigprocmask);
-    register_syscall!(0xb7, getcwd);
-    register_syscall!(0xbe, vfork);
-    register_syscall!(0xbf, getrlimit);
-    register_syscall!(0xc7, getuid);
-    register_syscall!(0xc8, getgid);
-    register_syscall!(0xc9, geteuid);
-    register_syscall!(0xca, geteuid);
-    register_syscall!(0xe0, gettid);
-    register_syscall!(0xee, tkill);
-    register_syscall!(0xf3, set_thread_area);
-    register_syscall!(0xfc, exit);
-    register_syscall!(0x102, set_tid_address);
-    register_syscall!(0x154, prlimit64);
-    register_syscall!(0x180, arch_prctl);
+// TODO: This should be for x86 only.
+#[eonix_macros::define_syscall(0x180)]
+fn arch_prctl(option: u32, addr: u32) -> KResult<u32> {
+    sys_arch_prctl(thread, option, addr)
 }

+ 9 - 23
src/kernel/syscall/sysinfo.rs

@@ -1,16 +1,12 @@
-use bindings::EINVAL;
-
 use crate::{
     kernel::{
-        constants::{CLOCK_MONOTONIC, CLOCK_REALTIME},
+        constants::{CLOCK_MONOTONIC, CLOCK_REALTIME, EINVAL},
         timer::ticks,
         user::UserPointerMut,
     },
     prelude::*,
 };
 
-use super::{define_syscall32, register_syscall};
-
 #[derive(Clone, Copy)]
 struct NewUTSName {
     sysname: [u8; 65],
@@ -63,7 +59,8 @@ pub struct TimeSpec {
     nsec: u64,
 }
 
-fn do_gettimeofday(timeval: *mut TimeVal, timezone: *mut ()) -> KResult<()> {
+#[eonix_macros::define_syscall(0x4e)]
+fn gettimeofday(timeval: *mut TimeVal, timezone: *mut ()) -> KResult<()> {
     if !timezone.is_null() {
         return Err(EINVAL);
     }
@@ -80,7 +77,8 @@ fn do_gettimeofday(timeval: *mut TimeVal, timezone: *mut ()) -> KResult<()> {
     Ok(())
 }
 
-fn do_clock_gettime64(clock_id: u32, timespec: *mut TimeSpec) -> KResult<()> {
+#[eonix_macros::define_syscall(0x193)]
+fn clock_gettime64(clock_id: u32, timespec: *mut TimeSpec) -> KResult<()> {
     if clock_id != CLOCK_REALTIME && clock_id != CLOCK_MONOTONIC {
         unimplemented!("Unsupported clock_id: {}", clock_id);
     }
@@ -111,7 +109,8 @@ struct Sysinfo {
     _padding: [u8; 8],
 }
 
-fn do_sysinfo(info: *mut Sysinfo) -> KResult<()> {
+#[eonix_macros::define_syscall(0x74)]
+fn sysinfo(info: *mut Sysinfo) -> KResult<()> {
     let info = UserPointerMut::new(info)?;
     info.write(Sysinfo {
         uptime: ticks().in_secs() as u32,
@@ -139,7 +138,8 @@ struct TMS {
     tms_cstime: u32,
 }
 
-fn do_times(tms: *mut TMS) -> KResult<()> {
+#[eonix_macros::define_syscall(0x2b)]
+fn times(tms: *mut TMS) -> KResult<()> {
     let tms = UserPointerMut::new(tms)?;
     tms.write(TMS {
         tms_utime: 0,
@@ -148,17 +148,3 @@ fn do_times(tms: *mut TMS) -> KResult<()> {
         tms_cstime: 0,
     })
 }
-
-define_syscall32!(sys_newuname, do_newuname, buffer: *mut NewUTSName);
-define_syscall32!(sys_gettimeofday, do_gettimeofday, timeval: *mut TimeVal, timezone: *mut ());
-define_syscall32!(sys_clock_gettime64, do_clock_gettime64, clock_id: u32, timespec: *mut TimeSpec);
-define_syscall32!(sys_sysinfo, do_sysinfo, info: *mut Sysinfo);
-define_syscall32!(sys_times, do_times, tms: *mut TMS);
-
-pub(super) fn register() {
-    register_syscall!(0x2b, times);
-    register_syscall!(0x4e, gettimeofday);
-    register_syscall!(0x74, sysinfo);
-    register_syscall!(0x7a, newuname);
-    register_syscall!(0x193, clock_gettime64);
-}

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

@@ -461,6 +461,14 @@ impl Process {
             needs_notify: false,
         }
     }
+
+    pub async fn force_kill(self: &Arc<Self>, signal: Signal) {
+        let mut proc_list = ProcessList::get().write().await;
+        unsafe {
+            // SAFETY: Preemption is disabled.
+            proc_list.do_kill_process(self, WaitType::Signaled(signal));
+        }
+    }
 }
 
 impl WaitList {

+ 4 - 16
src/kernel/task/process_list.rs

@@ -1,4 +1,6 @@
-use super::{Process, ProcessGroup, Session, Signal, Thread, WaitObject, WaitType};
+use core::sync::atomic::Ordering;
+
+use super::{Process, ProcessGroup, Session, Thread, WaitObject, WaitType};
 use crate::rcu::rcu_sync;
 use alloc::{
     collections::btree_map::BTreeMap,
@@ -49,21 +51,6 @@ impl ProcessList {
         self.threads.insert(thread.tid, thread.clone());
     }
 
-    pub fn kill_current(signal: Signal) -> ! {
-        unsafe {
-            let mut process_list = Task::block_on(ProcessList::get().write());
-
-            process_list.do_kill_process(&Thread::current().process, WaitType::Signaled(signal));
-        }
-
-        unsafe {
-            eonix_preempt::disable();
-
-            // SAFETY: Preempt count == 1.
-            Thread::exit();
-        }
-    }
-
     pub fn remove_process(&mut self, pid: u32) {
         // Thread group leader has the same tid as the pid.
         if let Some(thread) = self.threads.remove(&pid) {
@@ -130,6 +117,7 @@ impl ProcessList {
             assert!(thread.tid == Thread::current().tid);
             // TODO: Send SIGKILL to all threads.
             thread.files.close_all();
+            thread.dead.store(true, Ordering::SeqCst);
         }
 
         // If we are the session leader, we should drop the control terminal.

+ 23 - 27
src/kernel/task/signal.rs

@@ -5,9 +5,10 @@ mod signal_mask;
 use super::{ProcessList, Thread, WaitObject, WaitType};
 use crate::{kernel::user::UserPointer, prelude::*};
 use alloc::collections::binary_heap::BinaryHeap;
-use arch::{ExtendedContext, InterruptContext};
+use arch::{FpuState, TrapContext};
 use bindings::{EFAULT, EINVAL};
 use core::{cmp::Reverse, task::Waker};
+use eonix_hal::traits::trap::RawTrapContext;
 use eonix_runtime::task::Task;
 use eonix_sync::AsProof as _;
 use intrusive_collections::UnsafeRef;
@@ -17,6 +18,9 @@ pub use signal::{Signal, SIGNAL_IGNORE, SIGNAL_NOW, SIGNAL_STOP};
 pub use signal_action::SignalAction;
 pub use signal_mask::SignalMask;
 
+pub(self) const SAVED_DATA_SIZE: usize =
+    size_of::<TrapContext>() + size_of::<FpuState>() + size_of::<SignalMask>();
+
 struct SignalListInner {
     mask: SignalMask,
     pending: BinaryHeap<Reverse<Signal>>,
@@ -161,11 +165,7 @@ impl SignalList {
     }
 
     /// Handle signals in the context of `Thread::current()`.
-    ///
-    /// # Safety
-    /// This function might never return. Caller must make sure that local variables
-    /// that own resources are dropped before calling this function.
-    pub async fn handle(&self, int_stack: &mut InterruptContext, ext_ctx: &mut ExtendedContext) {
+    pub async fn handle(&self, trap_ctx: &mut TrapContext, fpu_state: &mut FpuState) {
         loop {
             let signal = {
                 let signal = match self.inner.lock().pop() {
@@ -182,7 +182,7 @@ impl SignalList {
                         old_mask
                     };
 
-                    let result = handler.handle(signal, old_mask, int_stack, ext_ctx);
+                    let result = handler.handle(signal, old_mask, trap_ctx, fpu_state);
                     if result.is_err() {
                         self.inner.lock().mask = old_mask;
                     }
@@ -245,29 +245,25 @@ impl SignalList {
                         );
                     }
                 }
-                // Default to terminate the process.
-                signal => ProcessList::kill_current(signal),
+                signal => {
+                    // Default to terminate the process.
+                    Thread::current().process.force_kill(signal).await;
+                    return;
+                }
             }
         }
     }
 
-    /// Load the signal mask, MMX registers and interrupt stack from the user stack.
-    /// We must be here because `sigreturn` is called. Se we return the value of the register
-    /// used to store the syscall return value to prevent the original value being clobbered.
-    pub fn restore(
-        &self,
-        int_stack: &mut InterruptContext,
-        ext_ctx: &mut ExtendedContext,
-    ) -> KResult<usize> {
-        let old_mask_vaddr = int_stack.rsp as usize;
-        let old_mmxregs_vaddr = old_mask_vaddr + size_of::<usize>();
-        let old_int_stack_vaddr = old_mmxregs_vaddr + size_of::<ExtendedContext>();
-
-        let old_mask = UserPointer::<u64>::new_vaddr(old_mask_vaddr)?.read()?;
-        *ext_ctx = UserPointer::<ExtendedContext>::new_vaddr(old_mmxregs_vaddr)?.read()?;
-        *int_stack = UserPointer::<InterruptContext>::new_vaddr(old_int_stack_vaddr)?.read()?;
-
-        self.inner.lock().mask = SignalMask::from(old_mask);
-        Ok(int_stack.rax as usize)
+    /// Load the signal mask, fpu state and trap context from the user stack.
+    pub fn restore(&self, trap_ctx: &mut TrapContext, fpu_state: &mut FpuState) -> KResult<()> {
+        let old_trap_ctx_vaddr = trap_ctx.get_stack_pointer();
+        let old_fpu_state_vaddr = old_trap_ctx_vaddr + size_of::<TrapContext>();
+        let old_mask_vaddr = old_fpu_state_vaddr + size_of::<FpuState>();
+
+        *trap_ctx = UserPointer::<TrapContext>::new_vaddr(old_trap_ctx_vaddr)?.read()?;
+        *fpu_state = UserPointer::<FpuState>::new_vaddr(old_fpu_state_vaddr)?.read()?;
+        self.inner.lock().mask = UserPointer::<SignalMask>::new_vaddr(old_mask_vaddr)?.read()?;
+
+        Ok(())
     }
 }

+ 40 - 40
src/kernel/task/signal/signal_action.rs

@@ -1,4 +1,4 @@
-use super::{KResult, Signal, SignalMask};
+use super::{KResult, Signal, SignalMask, SAVED_DATA_SIZE};
 use crate::{
     io::BufferFill as _,
     kernel::{
@@ -8,8 +8,9 @@ use crate::{
     SIGNAL_NOW,
 };
 use alloc::collections::btree_map::BTreeMap;
-use arch::{ExtendedContext, InterruptContext};
+use arch::{FpuState, TrapContext};
 use core::num::NonZero;
+use eonix_hal::traits::trap::RawTrapContext;
 use eonix_mm::address::{Addr as _, AddrOps as _, VAddr};
 use posix_types::signal::{SigAction, TryFromSigAction};
 
@@ -68,46 +69,45 @@ impl SignalAction {
         self,
         signal: Signal,
         old_mask: SignalMask,
-        int_stack: &mut InterruptContext,
-        ext_ctx: &mut ExtendedContext,
+        trap_ctx: &mut TrapContext,
+        fpu_state: &mut FpuState,
     ) -> KResult<()> {
-        // TODO: The sizes of the context structures should be arch-specific.
-        const CONTEXT_SIZE: usize = size_of::<InterruptContext>()
-            + size_of::<ExtendedContext>()
-            + size_of::<SignalMask>() // old_mask
-            + size_of::<u32>(); // `sa_handler` argument: `signum`
-
-        match self {
-            SignalAction::Default | SignalAction::Ignore => {
-                unreachable!("Default and Ignore actions should not be handled here")
-            }
+        let SignalAction::SimpleHandler {
+            handler, restorer, ..
+        } = self
+        else {
+            unreachable!("Default and Ignore actions should not be handled here");
+        };
+
+        let Some(restorer) = restorer else {
             // We don't accept signal handlers with no signal restorers for now.
-            SignalAction::SimpleHandler { restorer: None, .. } => Err(ENOSYS),
-            SignalAction::SimpleHandler {
-                handler,
-                restorer: Some(restorer),
-                ..
-            } => {
-                // Save current interrupt context to 128 bytes above current user stack
-                // and align to 16 bytes. Then we push the return address of the restorer.
-                // TODO!!!: Determine the size of the return address
-                let sp = VAddr::from(int_stack.rsp as usize - 128 - CONTEXT_SIZE).floor_to(16)
-                    - size_of::<u32>();
-                let restorer_address = restorer.addr() as u32;
-                let mut stack =
-                    UserBuffer::new(sp.addr() as *mut u8, CONTEXT_SIZE + size_of::<u32>())?;
-
-                stack.copy(&restorer_address)?.ok_or(EFAULT)?; // Restorer address
-                stack.copy(&u32::from(signal))?.ok_or(EFAULT)?; // `signum`
-                stack.copy(&old_mask)?.ok_or(EFAULT)?; // Original signal mask
-                stack.copy(ext_ctx)?.ok_or(EFAULT)?; // MMX registers
-                stack.copy(int_stack)?.ok_or(EFAULT)?; // Interrupt stack
-
-                int_stack.rip = handler.addr() as u64;
-                int_stack.rsp = sp.addr() as u64;
-                Ok(())
-            }
-        }
+            return Err(ENOSYS)?;
+        };
+
+        let current_sp = VAddr::from(trap_ctx.get_stack_pointer());
+
+        // Save current interrupt context to 128 bytes above current user stack
+        // (in order to keep away from x86 red zone) and align to 16 bytes.
+        let saved_data_addr = (current_sp - 128 - SAVED_DATA_SIZE).floor_to(16);
+
+        let mut saved_data_buffer =
+            UserBuffer::new(saved_data_addr.addr() as *mut u8, SAVED_DATA_SIZE)?;
+
+        saved_data_buffer.copy(trap_ctx)?.ok_or(EFAULT)?;
+        saved_data_buffer.copy(fpu_state)?.ok_or(EFAULT)?;
+        saved_data_buffer.copy(&old_mask)?.ok_or(EFAULT)?;
+
+        // We need to push the arguments to the handler and then save the return address.
+        let new_sp = saved_data_addr - 16 - 4;
+        let restorer_address = restorer.addr() as u32;
+
+        let mut stack = UserBuffer::new(new_sp.addr() as *mut u8, 4 + 4)?;
+        stack.copy(&restorer_address)?.ok_or(EFAULT)?; // Restorer address
+        stack.copy(&u32::from(signal))?.ok_or(EFAULT)?; // The argument to the handler
+
+        trap_ctx.set_program_counter(handler.addr());
+        trap_ctx.set_stack_pointer(new_sp.addr());
+        Ok(())
     }
 }
 

+ 136 - 112
src/kernel/task/thread.rs

@@ -5,35 +5,42 @@ use super::{
 use crate::{
     kernel::{
         cpu::local_cpu,
+        interrupt::default_irq_handler,
+        syscall::{syscall_handlers, SyscallHandler},
+        timer::timer_interrupt,
         user::dataflow::CheckedUserPointer,
         vfs::{filearray::FileArray, FsContext},
     },
     prelude::*,
 };
 use alloc::sync::Arc;
-use arch::{InterruptContext, UserTLS, _arch_fork_return};
+use arch::{FpuState, TrapContext, UserTLS};
+use atomic_unique_refcell::AtomicUniqueRefCell;
 use core::{
-    arch::asm,
     pin::Pin,
     ptr::NonNull,
-    sync::atomic::{AtomicUsize, Ordering},
+    sync::atomic::{AtomicBool, Ordering},
     task::Waker,
 };
-use eonix_mm::address::{Addr as _, VAddr};
-use eonix_runtime::{
-    context::ExecutionContext,
-    run::{Contexted, Run, RunState},
+use eonix_hal::{
+    traits::{
+        fault::Fault,
+        fpu::RawFpuState as _,
+        trap::{RawTrapContext, TrapType},
+    },
+    trap::TrapContextExt,
 };
+use eonix_mm::address::{Addr as _, VAddr};
+use eonix_runtime::run::{Contexted, Run, RunState};
 use eonix_sync::AsProofMut as _;
 use pointers::BorrowedArc;
 
-struct CurrentThread {
-    thread: NonNull<Thread>,
-    runnable: NonNull<ThreadRunnable>,
-}
-
 #[eonix_percpu::define_percpu]
-static CURRENT_THREAD: Option<CurrentThread> = None;
+static CURRENT_THREAD: Option<NonNull<Thread>> = None;
+
+pub struct ThreadRunnable {
+    thread: Arc<Thread>,
+}
 
 pub struct ThreadBuilder {
     tid: Option<u32>,
@@ -44,6 +51,9 @@ pub struct ThreadBuilder {
     signal_list: Option<SignalList>,
     tls: Option<UserTLS>,
     set_child_tid: Option<usize>,
+
+    trap_ctx: Option<TrapContext>,
+    fpu_state: Option<FpuState>,
 }
 
 #[derive(Debug)]
@@ -67,18 +77,13 @@ pub struct Thread {
     pub fs_context: Arc<FsContext>,
 
     pub signal_list: SignalList,
-    inner: Spin<ThreadInner>,
-}
 
-pub struct ThreadRunnable {
-    thread: Arc<Thread>,
-    /// Interrupt context for the thread initialization.
-    /// We store the kernel stack pointer in one of the fields for now.
-    ///
-    /// TODO: A better way to store the interrupt context.
-    interrupt_context: InterruptContext,
-    interrupt_stack_pointer: AtomicUsize,
-    return_context: ExecutionContext,
+    pub trap_ctx: AtomicUniqueRefCell<TrapContext>,
+    pub fpu_state: AtomicUniqueRefCell<FpuState>,
+
+    pub dead: AtomicBool,
+
+    inner: Spin<ThreadInner>,
 }
 
 #[repr(transparent)]
@@ -132,6 +137,8 @@ impl ThreadBuilder {
             signal_list: None,
             tls: None,
             set_child_tid: None,
+            trap_ctx: None,
+            fpu_state: None,
         }
     }
 
@@ -175,18 +182,44 @@ impl ThreadBuilder {
         self
     }
 
+    pub fn trap_ctx(mut self, trap_ctx: TrapContext) -> Self {
+        self.trap_ctx = Some(trap_ctx);
+        self
+    }
+
+    pub fn fpu_state(mut self, fpu_state: FpuState) -> Self {
+        self.fpu_state = Some(fpu_state);
+        self
+    }
+
+    pub fn entry(mut self, entry: VAddr, stack_pointer: VAddr) -> Self {
+        let mut trap_ctx = TrapContext::new();
+        trap_ctx.set_user_mode(true);
+        trap_ctx.set_program_counter(entry.addr());
+        trap_ctx.set_stack_pointer(stack_pointer.addr());
+        trap_ctx.set_interrupt_enabled(true);
+
+        self.trap_ctx = Some(trap_ctx);
+        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 {
         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())
             .name(inner.name.clone())
             .tls(inner.tls.clone())
             .set_child_tid(inner.set_child_tid)
+            .trap_ctx(trap_ctx)
+            .fpu_state(thread.fpu_state.borrow().clone())
     }
 
     pub fn build(self, process_list: &mut ProcessList) -> Arc<Thread> {
@@ -199,6 +232,8 @@ impl ThreadBuilder {
             .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);
 
         signal_list.clear_pending();
 
@@ -208,6 +243,9 @@ impl ThreadBuilder {
             files,
             fs_context,
             signal_list,
+            trap_ctx: AtomicUniqueRefCell::new(trap_ctx),
+            fpu_state: AtomicUniqueRefCell::new(fpu_state),
+            dead: AtomicBool::new(false),
             inner: Spin::new(ThreadInner {
                 name,
                 tls: self.tls,
@@ -225,14 +263,13 @@ impl Thread {
     pub fn current<'lt>() -> BorrowedArc<'lt, Self> {
         // SAFETY: We won't change the thread pointer in the current CPU when
         // we return here after some preemption.
-        let current: &Option<CurrentThread> = unsafe { CURRENT_THREAD.as_ref() };
-        let current = current.as_ref().expect("Current thread is not set");
+        let current = CURRENT_THREAD.get().expect("Current thread is not set");
 
         // SAFETY: We can only use the returned value when we are in the context of the thread.
-        unsafe { BorrowedArc::from_raw(current.thread) }
+        unsafe { BorrowedArc::from_raw(current) }
     }
 
-    pub fn raise(self: &Arc<Self>, signal: Signal) -> RaiseResult {
+    pub fn raise(&self, signal: Signal) -> RaiseResult {
         self.signal_list.raise(signal)
     }
 
@@ -279,81 +316,51 @@ impl Thread {
         self.inner.lock().name.clone()
     }
 
-    /// # Safety
-    /// This function needs to be called with preempt count == 1.
-    /// We won't return so clean all the resources before calling this.
-    pub unsafe fn exit() -> ! {
-        // SAFETY: We won't change the thread pointer in the current CPU when
-        // we return here after some preemption.
-        let current: &Option<CurrentThread> = unsafe { CURRENT_THREAD.as_ref() };
-        let current = current.as_ref().expect("Current thread is not set");
-
-        // SAFETY: We can only use the `run_context` when we are in the context of the thread.
-        let runnable = unsafe { current.runnable.as_ref() };
+    pub fn handle_syscall(&self, no: usize, args: [usize; 6]) -> Option<usize> {
+        match syscall_handlers().get(no) {
+            Some(SyscallHandler { handler, .. }) => handler(self, args),
+            None => {
+                println_warn!("Syscall {no}({no:#x}) isn't implemented.");
+                self.raise(Signal::SIGSYS);
+                None
+            }
+        }
+    }
 
-        runnable.return_context.switch_noreturn()
+    pub fn is_dead(&self) -> bool {
+        self.dead.load(Ordering::SeqCst)
     }
 }
 
 impl ThreadRunnable {
-    pub fn new(thread: Arc<Thread>, entry: VAddr, stack_pointer: VAddr) -> Self {
-        let mut interrupt_context = InterruptContext::default();
-        interrupt_context.set_return_address(entry.addr() as _, true);
-        interrupt_context.set_stack_pointer(stack_pointer.addr() as _, true);
-        interrupt_context.set_interrupt_enabled(true);
-
-        Self {
-            thread,
-            interrupt_context,
-            interrupt_stack_pointer: AtomicUsize::new(0),
-            return_context: ExecutionContext::new(),
-        }
-    }
-
-    pub fn from_context(thread: Arc<Thread>, interrupt_context: InterruptContext) -> Self {
-        Self {
-            thread,
-            interrupt_context,
-            interrupt_stack_pointer: AtomicUsize::new(0),
-            return_context: ExecutionContext::new(),
-        }
+    pub fn new(thread: Arc<Thread>) -> Self {
+        Self { thread }
     }
 }
 
 impl Contexted for ThreadRunnable {
     fn load_running_context(&self) {
-        let thread: &Thread = &self.thread;
-
-        match self.interrupt_stack_pointer.load(Ordering::Relaxed) {
-            0 => {}
-            sp => unsafe {
-                // SAFETY: Preemption is disabled.
-                arch::load_interrupt_stack(local_cpu(), sp as u64);
-            },
-        }
+        self.thread.process.mm_list.activate();
 
-        // SAFETY: Preemption is disabled.
-        unsafe {
-            // SAFETY: `self` and `thread` are valid and non-null.
-            let current_thread = CurrentThread {
-                thread: NonNull::new_unchecked(thread as *const _ as *mut _),
-                runnable: NonNull::new_unchecked(self as *const _ as *mut _),
-            };
+        let raw_ptr: *const Thread = &raw const *self.thread;
+        CURRENT_THREAD.set(NonNull::new(raw_ptr as *mut _));
 
+        unsafe {
             // SAFETY: Preemption is disabled.
-            CURRENT_THREAD.swap(Some(current_thread));
+            self.thread.load_thread_area32();
         }
 
-        thread.process.mm_list.activate();
-
         unsafe {
-            // SAFETY: Preemption is disabled.
-            thread.load_thread_area32();
+            let trap_ctx_ptr: *const TrapContext = &raw const *self.thread.trap_ctx.borrow();
+            // SAFETY:
+            arch::load_interrupt_stack(local_cpu(), trap_ctx_ptr.add(1).addr() as u64);
         }
     }
 
     fn restore_running_context(&self) {
         self.thread.process.mm_list.deactivate();
+
+        CURRENT_THREAD.set(None);
     }
 }
 
@@ -361,41 +368,58 @@ impl Run for ThreadRunnable {
     type Output = ();
 
     fn run(self: Pin<&mut Self>, _waker: &Waker) -> RunState<Self::Output> {
-        let mut task_context = ExecutionContext::new();
-        task_context.set_interrupt(false);
-        task_context.set_ip(_arch_fork_return as _);
-        task_context.set_sp(&self.interrupt_context as *const _ as _);
-
-        eonix_preempt::disable();
-
-        // TODO!!!!!: CHANGE THIS
-        let sp = unsafe {
-            let mut sp: usize;
-            asm!(
-                "mov %rsp, {0}",
-                out(reg) sp,
-                options(nomem, preserves_flags, att_syntax),
-            );
-            sp -= 512;
-            sp &= !0xf;
-
-            sp
-        };
+        let irq_state = arch::disable_irqs_save();
 
-        self.interrupt_stack_pointer.store(sp, Ordering::Relaxed);
+        let run_state = loop {
+            if self.thread.is_dead() {
+                break RunState::Finished(());
+            }
 
-        unsafe {
-            // SAFETY: Preemption is disabled.
-            arch::load_interrupt_stack(local_cpu(), sp as u64);
-        }
+            if self.thread.signal_list.has_pending_signal() {
+                self.thread.signal_list.handle(
+                    &mut self.thread.trap_ctx.borrow(),
+                    &mut self.thread.fpu_state.borrow(),
+                );
+            }
 
-        eonix_preempt::enable();
+            if self.thread.is_dead() {
+                break RunState::Finished(());
+            }
 
-        self.return_context.switch_to(&task_context);
+            unsafe {
+                // SAFETY: We are returning to the context of the user thread.
+                self.thread.trap_ctx.borrow().trap_return();
+            }
 
-        // We return here with preempt count == 1.
-        eonix_preempt::enable();
+            let trap_type = self.thread.trap_ctx.borrow().trap_type();
+            match trap_type {
+                TrapType::Fault(Fault::PageFault(err_code)) => {
+                    let addr = arch::get_page_fault_address();
+                    let mms = &self.thread.process.mm_list;
+                    mms.handle_user_page_fault(addr, err_code);
+                }
+                TrapType::Fault(Fault::BadAccess) => {
+                    self.thread.signal_list.raise(Signal::SIGSEGV);
+                }
+                TrapType::Fault(Fault::InvalidOp) => {
+                    self.thread.signal_list.raise(Signal::SIGILL);
+                }
+                TrapType::Fault(Fault::Unknown(_)) => unimplemented!("Unhandled fault"),
+                TrapType::Irq(irqno) => default_irq_handler(irqno),
+                TrapType::Timer => {
+                    timer_interrupt();
+
+                    break RunState::Running;
+                }
+                TrapType::Syscall { no, args } => {
+                    if let Some(retval) = self.thread.handle_syscall(no, args) {
+                        self.thread.trap_ctx.borrow().set_user_return_value(retval);
+                    }
+                }
+            }
+        };
 
-        RunState::Finished(())
+        irq_state.restore();
+        run_state
     }
 }

+ 0 - 7
src/kernel/timer.rs

@@ -1,6 +1,5 @@
 use super::interrupt::end_of_interrupt;
 use core::sync::atomic::{AtomicUsize, Ordering};
-use eonix_runtime::scheduler::Scheduler;
 
 static TICKS: AtomicUsize = AtomicUsize::new(0);
 
@@ -28,12 +27,6 @@ impl Ticks {
 pub fn timer_interrupt() {
     end_of_interrupt();
     TICKS.fetch_add(1, Ordering::Relaxed);
-
-    if eonix_preempt::count() == 0 {
-        // To make scheduler satisfied.
-        eonix_preempt::disable();
-        Scheduler::schedule();
-    }
 }
 
 pub fn ticks() -> Ticks {

+ 0 - 5
src/kernel/vfs/filearray.rs

@@ -7,7 +7,6 @@ use crate::{
     kernel::{
         console::get_console,
         constants::ENXIO,
-        task::Thread,
         vfs::{dentry::Dentry, file::Pipe, s_isdir, s_isreg},
         CharDevice,
     },
@@ -54,10 +53,6 @@ impl OpenFile {
 }
 
 impl FileArray {
-    pub fn get_current<'lt>() -> &'lt Arc<Self> {
-        &Thread::current().borrow().files
-    }
-
     pub fn new() -> Arc<Self> {
         Arc::new(FileArray {
             inner: Spin::new(FileArrayInner {

+ 0 - 5
src/kernel/vfs/mod.rs

@@ -1,4 +1,3 @@
-use super::task::Thread;
 use crate::prelude::*;
 use alloc::sync::Arc;
 use bindings::{dev_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG};
@@ -63,10 +62,6 @@ impl TimeSpec {
 }
 
 impl FsContext {
-    pub fn get_current<'lt>() -> &'lt Arc<Self> {
-        &Thread::current().borrow().fs_context
-    }
-
     pub fn global() -> &'static Arc<Self> {
         &GLOBAL_FS_CONTEXT
     }

+ 4 - 3
src/lib.rs

@@ -112,7 +112,6 @@ pub extern "C" fn kernel_init(early_kstack_pfn: PFN) -> ! {
 async fn init_process(early_kstack_pfn: PFN) {
     unsafe { Page::from_raw(early_kstack_pfn) };
 
-    kernel::syscall::register_syscalls();
     CharDevice::init().unwrap();
 
     // We might want the serial initialized as soon as possible.
@@ -163,7 +162,9 @@ async fn init_process(early_kstack_pfn: PFN) {
         elf.load(argv, envp).unwrap()
     };
 
-    let thread_builder = ThreadBuilder::new().name(Arc::from(*b"busybox"));
+    let thread_builder = ThreadBuilder::new()
+        .name(Arc::from(&b"busybox"[..]))
+        .entry(ip, sp);
 
     let mut process_list = Task::block_on(ProcessList::get().write());
     let (thread, process) = ProcessBuilder::new()
@@ -176,5 +177,5 @@ async fn init_process(early_kstack_pfn: PFN) {
     // TODO!!!: Remove this.
     thread.files.open_console();
 
-    Scheduler::get().spawn::<KernelStack, _>(ThreadRunnable::new(thread, ip, sp));
+    Scheduler::get().spawn::<KernelStack, _>(ThreadRunnable::new(thread));
 }