浏览代码

partial work of hal interface

greatbridf 8 月之前
父节点
当前提交
00252343e9

+ 39 - 0
Cargo.lock

@@ -27,6 +27,7 @@ name = "arch"
 version = "0.1.0"
 dependencies = [
  "cfg-if",
+ "eonix_hal_traits",
  "eonix_mm",
 ]
 
@@ -121,6 +122,33 @@ version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
 
+[[package]]
+name = "eonix_hal"
+version = "0.1.0"
+dependencies = [
+ "arch",
+ "eonix_hal_macros",
+ "eonix_hal_traits",
+ "eonix_percpu",
+ "eonix_preempt",
+]
+
+[[package]]
+name = "eonix_hal_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "eonix_hal_traits"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "eonix_log"
 version = "0.1.0"
@@ -128,6 +156,15 @@ dependencies = [
  "eonix_sync",
 ]
 
+[[package]]
+name = "eonix_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "eonix_mm"
 version = "0.1.0"
@@ -192,7 +229,9 @@ dependencies = [
  "bindgen",
  "bitflags",
  "buddy_allocator",
+ "eonix_hal",
  "eonix_log",
+ "eonix_macros",
  "eonix_mm",
  "eonix_percpu",
  "eonix_preempt",

+ 2 - 0
Cargo.toml

@@ -12,6 +12,8 @@ atomic_unique_refcell = { path = "./crates/atomic_unique_refcell", features = [
     "no_std",
 ] }
 buddy_allocator = { path = "./crates/buddy_allocator" }
+eonix_hal = { path = "./crates/eonix_hal" }
+eonix_macros = { path = "./macros" }
 eonix_mm = { path = "./crates/eonix_mm" }
 eonix_percpu = { path = "./crates/eonix_percpu" }
 eonix_preempt = { path = "./crates/eonix_preempt" }

+ 1 - 0
arch/Cargo.toml

@@ -4,5 +4,6 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+eonix_hal_traits = { path = "../crates/eonix_hal/eonix_hal_traits" }
 eonix_mm = { path = "../crates/eonix_mm" }
 cfg-if = "1.0"

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

@@ -0,0 +1,27 @@
+use core::arch::asm;
+use eonix_hal_traits::fpu::RawFpuState;
+
+#[repr(align(16))]
+pub struct FpuState([u8; 512]);
+
+impl RawFpuState for FpuState {
+    fn save(&mut self) {
+        unsafe {
+            asm!(
+                "fxsave ({0})",
+                in(reg) &mut self.0,
+                options(att_syntax, nostack, preserves_flags)
+            )
+        }
+    }
+
+    fn restore(&mut self) {
+        unsafe {
+            asm!(
+                "fxrstor ({0})",
+                in(reg) &mut self.0,
+                options(att_syntax, nostack, preserves_flags)
+            )
+        }
+    }
+}

+ 2 - 2
arch/src/x86_64/init.rs

@@ -52,8 +52,8 @@ pub struct CPU {
 }
 
 impl CPU {
-    pub fn new() -> Self {
-        let (interrupt_control, cpuid) = InterruptControl::new();
+    pub fn new(base: usize) -> Self {
+        let (interrupt_control, cpuid) = InterruptControl::new(base);
 
         Self {
             cpuid,

+ 4 - 113
arch/src/x86_64/interrupt.rs

@@ -90,7 +90,6 @@ global_asm!(
 
         mov %rbx, %rdi
         mov %rsp, %rsi
-        call interrupt_handler
 
     ISR_stub_restore:
         fxrstor (%rsp)
@@ -126,117 +125,13 @@ global_asm!(
         iretq
         .cfi_endproc
 
-    .altmacro
-    .macro build_isr_no_err name
-        .align 8
-        .globl ISR\name
-        .type  ISR\name @function
-        ISR\name:
-            .cfi_startproc
-            .cfi_signal_frame
-            .cfi_def_cfa_offset 0x08
-            .cfi_offset %rsp, 0x10
-
-            .cfi_same_value %rax
-            .cfi_same_value %rbx
-            .cfi_same_value %rcx
-            .cfi_same_value %rdx
-            .cfi_same_value %rdi
-            .cfi_same_value %rsi
-            .cfi_same_value %r8
-            .cfi_same_value %r9
-            .cfi_same_value %r10
-            .cfi_same_value %r11
-            .cfi_same_value %r12
-            .cfi_same_value %r13
-            .cfi_same_value %r14
-            .cfi_same_value %r15
-            .cfi_same_value %rbp
-
-            push %rbp # push placeholder for error code
-            .cfi_def_cfa_offset 0x10
-
-            call ISR_stub
-            .cfi_endproc
-    .endm
-
-    .altmacro
-    .macro build_isr_err name
-        .align 8
-        .globl ISR\name
-        .type  ISR\name @function
-        ISR\name:
-            .cfi_startproc
-            .cfi_signal_frame
-            .cfi_def_cfa_offset 0x10
-            .cfi_offset %rsp, 0x10
-
-            .cfi_same_value %rax
-            .cfi_same_value %rbx
-            .cfi_same_value %rcx
-            .cfi_same_value %rdx
-            .cfi_same_value %rdi
-            .cfi_same_value %rsi
-            .cfi_same_value %r8
-            .cfi_same_value %r9
-            .cfi_same_value %r10
-            .cfi_same_value %r11
-            .cfi_same_value %r12
-            .cfi_same_value %r13
-            .cfi_same_value %r14
-            .cfi_same_value %r15
-            .cfi_same_value %rbp
-
-            call ISR_stub
-            .cfi_endproc
-    .endm
-
-    build_isr_no_err 0
-    build_isr_no_err 1
-    build_isr_no_err 2
-    build_isr_no_err 3
-    build_isr_no_err 4
-    build_isr_no_err 5
-    build_isr_no_err 6
-    build_isr_no_err 7
-    build_isr_err    8
-    build_isr_no_err 9
-    build_isr_err    10
-    build_isr_err    11
-    build_isr_err    12
-    build_isr_err    13
-    build_isr_err    14
-    build_isr_no_err 15
-    build_isr_no_err 16
-    build_isr_err    17
-    build_isr_no_err 18
-    build_isr_no_err 19
-    build_isr_no_err 20
-    build_isr_err    21
-    build_isr_no_err 22
-    build_isr_no_err 23
-    build_isr_no_err 24
-    build_isr_no_err 25
-    build_isr_no_err 26
-    build_isr_no_err 27
-    build_isr_no_err 28
-    build_isr_err    29
-    build_isr_err    30
-    build_isr_no_err 31
-
-    .set i, 32
-    .rept 0x80+1
-        build_isr_no_err %i
-        .set i, i+1
-    .endr
-
     .section .rodata
 
     .align 8
     .globl ISR_START_ADDR
     .type  ISR_START_ADDR @object
     ISR_START_ADDR:
-        .quad ISR0
+        .quad 0
     ",
     options(att_syntax),
 );
@@ -426,14 +321,10 @@ impl APICRegs {
 impl InterruptControl {
     /// # Return
     /// Returns a tuple of InterruptControl and the cpu id of the current cpu.
-    pub(crate) fn new() -> (Self, usize) {
-        extern "C" {
-            static ISR_START_ADDR: usize;
-        }
-
+    pub(crate) fn new(base: usize) -> (Self, usize) {
         let idt = core::array::from_fn(|idx| match idx {
-            0..0x80 => IDTEntry::new(unsafe { ISR_START_ADDR } + 8 * idx, 0x08, 0x8e),
-            0x80 => IDTEntry::new(unsafe { ISR_START_ADDR } + 8 * idx, 0x08, 0xee),
+            0..0x80 => IDTEntry::new(base + 8 * idx, 0x08, 0x8e),
+            0x80 => IDTEntry::new(base + 8 * idx, 0x08, 0xee),
             _ => IDTEntry::null(),
         });
 

+ 4 - 2
arch/src/x86_64/mod.rs

@@ -1,12 +1,13 @@
 mod context;
 mod fence;
+mod fpu;
 mod gdt;
 mod init;
 mod interrupt;
 mod io;
 mod mm;
 mod percpu;
-// mod trap;
+mod trap;
 mod user;
 
 use core::arch::asm;
@@ -20,9 +21,10 @@ pub use self::interrupt::*;
 pub use self::io::*;
 pub use self::user::*;
 pub use fence::*;
+pub use fpu::*;
 pub use mm::*;
 pub use percpu::*;
-// pub use trap::*;
+pub use trap::*;
 
 #[inline(always)]
 pub fn flush_tlb(vaddr: usize) {

+ 125 - 0
arch/src/x86_64/trap.rs

@@ -0,0 +1,125 @@
+use eonix_hal_traits::{
+    fault::{Fault, PageFaultErrorCode},
+    trap::{RawTrapContext, TrapType},
+};
+
+#[derive(Default)]
+#[repr(C, align(16))]
+pub struct TrapContext {
+    rax: u64,
+    rbx: u64,
+    rcx: u64,
+    rdx: u64,
+    rdi: u64,
+    rsi: u64,
+    r8: u64,
+    r9: u64,
+    r10: u64,
+    r11: u64,
+    r12: u64,
+    r13: u64,
+    r14: u64,
+    r15: u64,
+    rbp: u64,
+    int_no: u64,
+    errcode: u64,
+    rip: u64,
+    cs: u64,
+    flags: u64,
+    rsp: u64,
+    ss: u64,
+}
+
+impl TrapContext {
+    fn get_fault_type(&self) -> Fault {
+        match self.int_no {
+            6 | 8 => Fault::InvalidOp,
+            13 => Fault::BadAccess,
+            14 => {
+                let mut error_code = PageFaultErrorCode::empty();
+                if self.errcode & 1 != 0 {
+                    error_code |= PageFaultErrorCode::NonPresent;
+                }
+
+                if self.errcode & 2 != 0 {
+                    error_code |= PageFaultErrorCode::Write;
+                } else if self.errcode & 16 != 0 {
+                    error_code |= PageFaultErrorCode::InstructionFetch;
+                } else {
+                    error_code |= PageFaultErrorCode::Read;
+                }
+
+                if self.errcode & 4 != 0 {
+                    error_code |= PageFaultErrorCode::UserAccess;
+                }
+
+                Fault::PageFault(error_code)
+            }
+            code @ 0..0x20 => Fault::Unknown(code as usize),
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl RawTrapContext for TrapContext {
+    fn new() -> Self {
+        Self {
+            ..Default::default()
+        }
+    }
+
+    fn trap_type(&self) -> TrapType {
+        match self.int_no {
+            0..0x20 => TrapType::Fault(self.get_fault_type()),
+            0x40 => TrapType::Timer,
+            0x80 => TrapType::Syscall {
+                no: self.rax as usize,
+                args: [
+                    self.rbx as usize,
+                    self.rcx as usize,
+                    self.rdx as usize,
+                    self.rsi as usize,
+                    self.rdi as usize,
+                    self.rbp as usize,
+                ],
+            },
+            no => TrapType::Irq(no as usize - 0x20),
+        }
+    }
+
+    fn get_program_counter(&self) -> usize {
+        self.rip as usize
+    }
+
+    fn get_stack_pointer(&self) -> usize {
+        self.rsp as usize
+    }
+
+    fn set_program_counter(&mut self, pc: usize) {
+        self.rip = pc as u64
+    }
+
+    fn set_stack_pointer(&mut self, sp: usize) {
+        self.rsp = sp as u64
+    }
+
+    fn is_interrupt_enabled(&self) -> bool {
+        self.flags & 0x200 != 0
+    }
+
+    fn set_interrupt_enabled(&mut self, enabled: bool) {
+        if enabled {
+            self.flags |= 0x200;
+        } else {
+            self.flags &= !0x200;
+        }
+    }
+
+    fn is_user_mode(&self) -> bool {
+        self.cs & 3 == 3
+    }
+
+    fn set_user_return_value(&mut self, retval: usize) {
+        self.rax = retval as u64;
+    }
+}

+ 12 - 0
crates/eonix_hal/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "eonix_hal"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+eonix_hal_traits = { path = "./eonix_hal_traits" }
+eonix_hal_macros = { path = "./eonix_hal_macros" }
+
+arch = { path = "../../arch" }
+eonix_percpu = { path = "../eonix_percpu" }
+eonix_preempt = { path = "../eonix_preempt" }

+ 12 - 0
crates/eonix_hal/eonix_hal_macros/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "eonix_hal_macros"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = { version = "2.0", features = ["full"] }

+ 40 - 0
crates/eonix_hal/eonix_hal_macros/src/lib.rs

@@ -0,0 +1,40 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse_macro_input, ItemFn};
+
+/// Define the default trap handler. The function should take exactly one argument
+/// of type `&mut TrapContext`.
+///
+/// # Usage
+/// ```no_run
+/// #[eonix_hal::default_trap_handler]
+/// fn interrupt_handler(ctx: &mut TrapContext) {
+///     println!("Trap {} received!", ctx.trap_no());
+///     // ...
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn default_trap_handler(attrs: TokenStream, item: TokenStream) -> TokenStream {
+    if !attrs.is_empty() {
+        panic!("`default_trap_handler` attribute does not take any arguments");
+    }
+
+    let item = parse_macro_input!(item as ItemFn);
+
+    if item.sig.inputs.len() > 1 {
+        panic!("`default_trap_handler` only takes one argument");
+    }
+
+    let attrs = &item.attrs;
+    let arg = item.sig.inputs.first().unwrap();
+    let block = &item.block;
+
+    quote! {
+        #(#attrs)*
+        #[no_mangle]
+        pub unsafe extern "C" fn _default_trap_handler(#arg) #block
+    }
+    .into()
+}

+ 7 - 0
crates/eonix_hal/eonix_hal_traits/Cargo.toml

@@ -0,0 +1,7 @@
+[package]
+name = "eonix_hal_traits"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+bitflags = "2.6.0"

+ 20 - 0
crates/eonix_hal/eonix_hal_traits/src/fault.rs

@@ -0,0 +1,20 @@
+use bitflags::bitflags;
+
+bitflags! {
+    #[derive(Debug)]
+    pub struct PageFaultErrorCode: u32 {
+        const NonPresent = 1;
+        const Read = 2;
+        const Write = 4;
+        const InstructionFetch = 8;
+        const UserAccess = 16;
+    }
+}
+
+#[derive(Debug)]
+pub enum Fault {
+    InvalidOp,
+    BadAccess,
+    PageFault(PageFaultErrorCode),
+    Unknown(usize),
+}

+ 5 - 0
crates/eonix_hal/eonix_hal_traits/src/fpu.rs

@@ -0,0 +1,5 @@
+#[doc(notable_trait)]
+pub trait RawFpuState {
+    fn save(&mut self);
+    fn restore(&mut self);
+}

+ 6 - 0
crates/eonix_hal/eonix_hal_traits/src/lib.rs

@@ -0,0 +1,6 @@
+#![no_std]
+#![feature(doc_notable_trait)]
+
+pub mod fault;
+pub mod fpu;
+pub mod trap;

+ 79 - 0
crates/eonix_hal/eonix_hal_traits/src/trap.rs

@@ -0,0 +1,79 @@
+use crate::fault::Fault;
+use core::marker::PhantomData;
+
+/// A raw trap context.
+///
+/// This should be implemented by the architecture-specific trap context
+/// and will be used in the HAL crates.
+#[doc(notable_trait)]
+pub trait RawTrapContext {
+    fn new() -> Self;
+
+    fn trap_type(&self) -> TrapType;
+
+    fn get_program_counter(&self) -> usize;
+    fn get_stack_pointer(&self) -> usize;
+
+    fn set_program_counter(&mut self, pc: usize);
+    fn set_stack_pointer(&mut self, sp: usize);
+
+    fn is_interrupt_enabled(&self) -> bool;
+    fn set_interrupt_enabled(&mut self, enabled: bool);
+
+    fn is_user_mode(&self) -> bool;
+    fn set_user_return_value(&mut self, retval: usize);
+}
+
+/// The reason that caused the trap.
+pub enum TrapType {
+    Syscall { no: usize, args: [usize; 6] },
+    Fault(Fault),
+    Irq(usize),
+    Timer,
+}
+
+/// A marker type that indicates that the type is a raw trap context.
+///
+/// # Usage
+///
+/// Check whether a type implements `RawTrapContext` using a `PhantomData` field.
+///
+/// The following code should fail to compile:
+///
+/// ```compile_fail
+/// # use eonix_hal_traits::trap::IsRawTrapContext;
+/// struct NonRawTrapContext; // Does not implement `RawTrapContext`!
+///
+/// // Compile-time error: `NonRawTrapContext` does not implement `RawTrapContext`.
+/// struct UserStruct(NonRawTrapContext, IsRawTrapContext<NonRawTrapContext>);
+/// ```
+///
+/// While the following code should compile:
+///
+/// ```
+/// # use eonix_hal_traits::trap::IsRawTrapContext;
+/// struct RawTrapContextType;
+///
+/// impl RawTrapContext for RawTrapContextType {
+///     // ...
+/// #   fn trap_no(&self) -> usize {
+/// #       0
+/// #   }
+/// #   fn get_program_counter(&self) -> usize {
+/// #       0
+/// #   }
+/// #   fn get_stack_pointer(&self) -> usize {
+/// #       0
+/// #   }
+/// #   fn set_program_counter(&mut self, pc: usize) {}
+/// #   fn set_stack_pointer(&mut self, sp: usize) {}
+/// #   fn is_user_mode(&self) -> bool {
+/// #       false
+/// #   }
+/// }
+///
+/// struct UserStruct(RawTrapContextType, IsRawTrapContext<RawTrapContextType>);
+/// ```
+pub struct IsRawTrapContext<T>(PhantomData<T>)
+where
+    T: RawTrapContext;

+ 9 - 0
crates/eonix_hal/src/lib.rs

@@ -0,0 +1,9 @@
+#![no_std]
+#![feature(doc_notable_trait)]
+#![feature(naked_functions)]
+
+pub mod processor;
+pub mod trap;
+
+pub use eonix_hal_macros::default_trap_handler;
+pub use eonix_hal_traits as traits;

+ 1 - 0
crates/eonix_hal/src/processor.rs

@@ -0,0 +1 @@
+

+ 3 - 0
crates/eonix_hal/src/trap.rs

@@ -0,0 +1,3 @@
+mod trap_context;
+
+pub use trap_context::{init, TrapContextExt, TRAP_STUBS_START};

+ 368 - 0
crates/eonix_hal/src/trap/trap_context.rs

@@ -0,0 +1,368 @@
+use arch::{TaskContext, TrapContext};
+use core::{
+    arch::{global_asm, naked_asm},
+    mem::transmute,
+};
+use eonix_hal_traits::trap::IsRawTrapContext;
+
+#[doc(notable_trait)]
+pub trait TrapContextExt {
+    /// Return to the context before the trap occurred.
+    ///
+    /// # Safety
+    /// This function is unsafe because the caller MUST ensure that the
+    /// context before the trap is valid, that is, that the stack pointer
+    /// points to a valid stack frame and the program counter points to some
+    /// valid instruction.
+    unsafe fn trap_return(&mut self);
+}
+
+struct _CheckTrapContext(IsRawTrapContext<TrapContext>);
+
+unsafe extern "C" {
+    fn _default_trap_handler(trap_context: &mut TrapContext);
+    pub static TRAP_STUBS_START: usize;
+    fn _raw_trap_return();
+}
+
+#[eonix_percpu::define_percpu]
+static TRAP_HANDLER: usize = 0;
+
+#[eonix_percpu::define_percpu]
+static CAPTURER_CONTEXT: TaskContext = TaskContext::new();
+
+/// This value will never be used.
+static mut DIRTY_TRAP_CONTEXT: TaskContext = TaskContext::new();
+
+global_asm!(
+    r"
+    .set RAX, 0x00
+    .set RBX, 0x08
+    .set RCX, 0x10
+    .set RDX, 0x18
+    .set RDI, 0x20
+    .set RSI, 0x28
+    .set R8, 0x30
+    .set R9, 0x38
+    .set R10, 0x40
+    .set R11, 0x48
+    .set R12, 0x50
+    .set R13, 0x58
+    .set R14, 0x60
+    .set R15, 0x68
+    .set RBP, 0x70
+    .set INT_NO, 0x78
+    .set ERRCODE, 0x80
+    .set RIP, 0x88
+    .set CS, 0x90
+    .set FLAGS, 0x98
+    .set RSP, 0xa0
+    .set SS, 0xa8
+
+    .macro cfi_all_same_value
+        .cfi_same_value %rax
+        .cfi_same_value %rbx
+        .cfi_same_value %rcx
+        .cfi_same_value %rdx
+        .cfi_same_value %rdi
+        .cfi_same_value %rsi
+        .cfi_same_value %r8
+        .cfi_same_value %r9
+        .cfi_same_value %r10
+        .cfi_same_value %r11
+        .cfi_same_value %r12
+        .cfi_same_value %r13
+        .cfi_same_value %r14
+        .cfi_same_value %r15
+        .cfi_same_value %rbp
+    .endm
+
+    .globl TRAP_STUBS_START
+    TRAP_STUBS_START:
+        .quad _trap_stubs_start
+
+    _trap_stubs_start:
+        .altmacro
+        .macro build_isr_no_err name
+            .align 8
+            .globl ISR\name
+            .type  ISR\name @function
+            ISR\name:
+                .cfi_startproc
+                .cfi_signal_frame
+                .cfi_def_cfa_offset 0x08
+                .cfi_offset %rsp, 0x10
+
+                cfi_all_same_value
+
+                push %rbp # push placeholder for error code
+                .cfi_def_cfa_offset 0x10
+
+                call _raw_trap_entry
+                .cfi_endproc
+        .endm
+
+        .altmacro
+        .macro build_isr_err name
+            .align 8
+            .globl ISR\name
+            .type  ISR\name @function
+            ISR\name:
+                .cfi_startproc
+                .cfi_signal_frame
+                .cfi_def_cfa_offset 0x10
+                .cfi_offset %rsp, 0x10
+
+                cfi_all_same_value
+
+                call _raw_trap_entry
+                .cfi_endproc
+        .endm
+
+        build_isr_no_err 0
+        build_isr_no_err 1
+        build_isr_no_err 2
+        build_isr_no_err 3
+        build_isr_no_err 4
+        build_isr_no_err 5
+        build_isr_no_err 6
+        build_isr_no_err 7
+        build_isr_err    8
+        build_isr_no_err 9
+        build_isr_err    10
+        build_isr_err    11
+        build_isr_err    12
+        build_isr_err    13
+        build_isr_err    14
+        build_isr_no_err 15
+        build_isr_no_err 16
+        build_isr_err    17
+        build_isr_no_err 18
+        build_isr_no_err 19
+        build_isr_no_err 20
+        build_isr_err    21
+        build_isr_no_err 22
+        build_isr_no_err 23
+        build_isr_no_err 24
+        build_isr_no_err 25
+        build_isr_no_err 26
+        build_isr_no_err 27
+        build_isr_no_err 28
+        build_isr_err    29
+        build_isr_err    30
+        build_isr_no_err 31
+
+        .set i, 32
+        .rept 0x80+1
+            build_isr_no_err %i
+            .set i, i+1
+        .endr
+    
+    .globl _raw_trap_entry
+    .type  _raw_trap_entry @function
+    _raw_trap_entry:
+        .cfi_startproc
+        .cfi_signal_frame
+        .cfi_def_cfa %rsp, 0x18
+        .cfi_offset %rsp, 0x10
+
+        cfi_all_same_value
+        
+        sub $0x78, %rsp
+        .cfi_def_cfa_offset CS
+        
+        mov %rax, RAX(%rsp)
+        .cfi_rel_offset %rax, RAX
+        mov %rbx, RBX(%rsp)
+        .cfi_rel_offset %rbx, RBX
+        mov %rcx, RCX(%rsp)
+        .cfi_rel_offset %rcx, RCX
+        mov %rdx, RDX(%rsp)
+        .cfi_rel_offset %rdx, RDX
+        mov %rdi, RDI(%rsp)
+        .cfi_rel_offset %rdi, RDI
+        mov %rsi, RSI(%rsp)
+        .cfi_rel_offset %rsi, RSI
+        mov %r8, R8(%rsp)
+        .cfi_rel_offset %r8, R8
+        mov %r9, R9(%rsp)
+        .cfi_rel_offset %r9, R9
+        mov %r10, R10(%rsp)
+        .cfi_rel_offset %r10, R10
+        mov %r11, R11(%rsp)
+        .cfi_rel_offset %r11, R11
+        mov %r12, R12(%rsp)
+        .cfi_rel_offset %r12, R12
+        mov %r13, R13(%rsp)
+        .cfi_rel_offset %r13, R13
+        mov %r14, R14(%rsp)
+        .cfi_rel_offset %r14, R14
+        mov %r15, R15(%rsp)
+        .cfi_rel_offset %r15, R15
+        mov %rbp, RBP(%rsp)
+        .cfi_rel_offset %rbp, RBP
+        
+        mov INT_NO(%rsp), %rcx
+        sub $_trap_stubs_start, %rcx
+        shr $3, %rcx
+        mov %rcx, INT_NO(%rsp)
+        
+        cmpq $0x08, CS(%rsp)
+        je 2f
+        swapgs
+        
+        2:
+        mov %gs:0, %rcx
+        add ${handler}, %rcx
+        mov (%rcx), %rcx
+        
+        jmp *%rcx
+        .cfi_endproc
+    
+    _raw_trap_return:
+        .cfi_startproc
+        .cfi_def_cfa %rsp, CS
+        .cfi_rel_offset %rax, RAX
+        .cfi_rel_offset %rbx, RBX
+        .cfi_rel_offset %rcx, RCX
+        .cfi_rel_offset %rdx, RDX
+        .cfi_rel_offset %rdi, RDI
+        .cfi_rel_offset %rsi, RSI
+        .cfi_rel_offset %r8, R8
+        .cfi_rel_offset %r9, R9
+        .cfi_rel_offset %r10, R10
+        .cfi_rel_offset %r11, R11
+        .cfi_rel_offset %r12, R12
+        .cfi_rel_offset %r13, R13
+        .cfi_rel_offset %r14, R14
+        .cfi_rel_offset %r15, R15
+        .cfi_rel_offset %rbp, RBP
+        .cfi_rel_offset %rsp, RSP
+        
+        mov RAX(%rsp), %rax
+        .cfi_restore %rax
+        mov RBX(%rsp), %rbx
+        .cfi_restore %rbx
+        mov RCX(%rsp), %rcx
+        .cfi_restore %rcx
+        mov RDX(%rsp), %rdx
+        .cfi_restore %rdx
+        mov RDI(%rsp), %rdi
+        .cfi_restore %rdi
+        mov RSI(%rsp), %rsi
+        .cfi_restore %rsi
+        mov R8(%rsp), %r8
+        .cfi_restore %r8
+        mov R9(%rsp), %r9
+        .cfi_restore %r9
+        mov R10(%rsp), %r10
+        .cfi_restore %r10
+        mov R11(%rsp), %r11
+        .cfi_restore %r11
+        mov R12(%rsp), %r12
+        .cfi_restore %r12
+        mov R13(%rsp), %r13
+        .cfi_restore %r13
+        mov R14(%rsp), %r14
+        .cfi_restore %r14
+        mov R15(%rsp), %r15
+        .cfi_restore %r15
+        mov RBP(%rsp), %rbp
+        .cfi_restore %rbp
+        
+        cmpq $0x08, CS(%rsp)
+        je 2f
+        swapgs
+        
+        2:
+        lea RIP(%rsp), %rsp
+        .cfi_def_cfa %rsp, 0x08
+        .cfi_offset %rsp, 0x10
+        
+        iretq
+        .cfi_endproc
+    ",
+    handler = sym _percpu_inner_TRAP_HANDLER,
+    options(att_syntax),
+);
+
+/// Default handler handles the trap on the current stack and returns
+/// to the context before interrut.
+#[naked]
+unsafe extern "C" fn default_trap_handler() {
+    unsafe {
+        naked_asm!(
+            ".cfi_startproc",
+            "mov %rsp, %rbx",
+            ".cfi_def_cfa_register %rbx",
+            "",
+            "and $~0xf, %rsp",
+            "",
+            "mov %rbx, %rdi",
+            "call {handle_trap}",
+            "",
+            "mov %rbx, %rsp",
+            ".cfi_def_cfa_register %rsp",
+            "",
+            "jmp {trap_return}",
+            ".cfi_endproc",
+            handle_trap = sym _default_trap_handler,
+            trap_return = sym _raw_trap_return,
+            options(att_syntax),
+        );
+    }
+}
+
+#[naked]
+unsafe extern "C" fn captured_trap_handler() {
+    unsafe {
+        naked_asm!(
+            "mov ${from_context}, %rdi",
+            "mov ${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,
+            switch = sym arch::TaskContext::switch,
+            options(att_syntax),
+        );
+    }
+}
+
+#[naked]
+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),
+        );
+    }
+}
+
+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 mut to_ctx = arch::TaskContext::new();
+        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);
+        }
+
+        TRAP_HANDLER.set(old_handler);
+        irq_states.restore();
+    }
+}
+
+pub fn init() {
+    let addr = unsafe { transmute::<_, usize>(default_trap_handler as *const ()) };
+    TRAP_HANDLER.set(addr as usize);
+}

+ 2 - 0
crates/eonix_mm/src/address/addr.rs

@@ -1,6 +1,7 @@
 use crate::paging::PAGE_SIZE;
 use core::ops::{Add, Sub};
 
+#[doc(notable_trait)]
 pub trait Addr:
     Sized
     + Copy
@@ -17,6 +18,7 @@ pub trait Addr:
     fn addr(self) -> usize;
 }
 
+#[doc(notable_trait)]
 pub trait AddrOps: Sized {
     fn offset_in(self, size: usize) -> usize;
 

+ 1 - 0
crates/eonix_mm/src/lib.rs

@@ -1,4 +1,5 @@
 #![no_std]
+#![feature(doc_notable_trait)]
 
 pub mod address;
 pub mod page_table;

+ 2 - 0
crates/eonix_mm/src/page_table/pte.rs

@@ -27,6 +27,7 @@ bitflags! {
     }
 }
 
+#[doc(notable_trait)]
 pub trait RawAttribute: Copy + From<PageAttribute> + From<TableAttribute> {
     /// Create a new attribute representing a non-present page.
     fn null() -> Self;
@@ -46,6 +47,7 @@ pub trait RawAttribute: Copy + From<PageAttribute> + From<TableAttribute> {
     fn as_page_attr(self) -> Option<PageAttribute>;
 }
 
+#[doc(notable_trait)]
 pub trait PTE: Sized {
     type Attr: RawAttribute;
 

+ 1 - 0
crates/eonix_mm/src/paging/page.rs

@@ -14,6 +14,7 @@ pub const PAGE_SIZE_BITS: u32 = PAGE_SIZE.trailing_zeros();
 pub struct PageBlock([u8; PAGE_SIZE]);
 
 /// A trait that provides the kernel access to the page.
+#[doc(notable_trait)]
 pub trait PageAccess {
     /// Returns a kernel-accessible pointer to the page referenced by the given
     /// physical frame number.

+ 2 - 0
crates/eonix_mm/src/paging/page_alloc.rs

@@ -6,6 +6,7 @@ use super::{raw_page::UnmanagedRawPage, RawPage};
 /// behavior, meaning that the allocators are to be passed around by value and stored in
 /// managed data structures. This is because the allocator may be used to deallocate the
 /// pages it allocates.
+#[doc(notable_trait)]
 pub trait PageAlloc: Clone {
     type RawPage: RawPage;
 
@@ -37,6 +38,7 @@ pub trait PageAlloc: Clone {
 /// A trait for global page allocators.
 ///
 /// Global means that we can get an instance of the allocator from anywhere in the kernel.
+#[doc(notable_trait)]
 pub trait GlobalPageAlloc: PageAlloc + 'static {
     /// Get the global page allocator.
     fn global() -> Self;

+ 1 - 0
crates/eonix_mm/src/paging/raw_page.rs

@@ -3,6 +3,7 @@ use core::sync::atomic::AtomicUsize;
 
 /// A `RawPage` represents a page of memory in the kernel. It is a low-level
 /// representation of a page that is used by the kernel to manage memory.
+#[doc(notable_trait)]
 pub trait RawPage: Clone + Copy + From<PFN> + Into<PFN> {
     fn order(&self) -> u32;
     fn refcount(&self) -> &AtomicUsize;

+ 12 - 0
macros/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "eonix_macros"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = { version = "2.0", features = ["full"] }

+ 140 - 0
macros/src/lib.rs

@@ -0,0 +1,140 @@
+extern crate proc_macro;
+
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{parse2, FnArg, Ident, ItemFn, LitInt, LitStr};
+
+fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
+    if attrs.is_empty() {
+        panic!("`define_syscall` attribute should take one argument: `syscall_no`");
+    }
+
+    let syscall_no = parse2::<LitInt>(attrs).expect("Invalid syscall number");
+    let syscall_no = syscall_no
+        .base10_parse::<usize>()
+        .expect("Invalid syscall number");
+
+    assert!(syscall_no < 512, "Syscall number must be less than 512");
+
+    let item = parse2::<ItemFn>(item).unwrap();
+
+    let attrs = item.attrs;
+    let vis = item.vis;
+
+    let args = item.sig.inputs.iter();
+    let ty_ret = item.sig.output;
+
+    let args_mapped = item
+        .sig
+        .inputs
+        .iter()
+        .enumerate()
+        .map(|(idx, arg)| match arg {
+            FnArg::Receiver(_) => panic!("&self is not permitted."),
+            FnArg::Typed(arg) => {
+                let arg_ident = Ident::new(&format!("arg_{}", idx), Span::call_site());
+                let arg_ty = &arg.ty;
+                quote! { let #arg_ident: #arg_ty = <#arg_ty>::from_arg(args[#idx]); }
+            }
+        });
+
+    let args_call = item
+        .sig
+        .inputs
+        .iter()
+        .enumerate()
+        .map(|(idx, arg)| match arg {
+            FnArg::Receiver(_) => panic!("&self is not permitted."),
+            FnArg::Typed(_) => {
+                let arg_ident = Ident::new(&format!("arg_{}", idx), Span::call_site());
+                quote! { #arg_ident }
+            }
+        });
+
+    let syscall_name = item.sig.ident;
+    let syscall_name_str = LitStr::new(&syscall_name.to_string(), Span::call_site());
+    let body = item.block;
+
+    let helper_fn = Ident::new(&format!("_do_syscall_{}", syscall_name), Span::call_site());
+    let helper_fn_pointer = Ident::new(
+        &format!("SYSCALL_ENTRY_{:03}", syscall_no),
+        Span::call_site(),
+    );
+
+    let real_fn = Ident::new(&format!("sys_{}", syscall_name), Span::call_site());
+
+    quote! {
+        #[used]
+        #[doc(hidden)]
+        #[link_section = ".syscalls"]
+        static #helper_fn_pointer: crate::kernel::syscall::SyscallHandler =
+            crate::kernel::syscall::SyscallHandler {
+                handler: #helper_fn,
+                name: #syscall_name_str,
+            };
+
+        fn #helper_fn (
+            thd: &crate::kernel::task::Thread,
+            trap_ctx: &mut arch::TrapContext,
+            args: [usize; 6]
+        ) -> usize {
+            use crate::kernel::syscall::{FromSyscallArg, SyscallRetVal};
+
+            #(#args_mapped)*
+
+            // eonix_log::println_trace!(
+            //     "trace_syscall",
+            //     "tid{}: {}({}) => {{",
+            //     crate::kernel::task::Thread::current().tid,
+            //     #syscall_name_str,
+            //     crate::kernel::syscall::format_expand!($($arg, $arg),*),
+            // );
+
+            let retval = #real_fn(thd, trap_ctx, #(#args_call),*).into_retval();
+
+            // eonix_log::println_trace!(
+            //     "trace_syscall",
+            //     "tid{}: {}({}) => {{",
+            //     crate::kernel::task::Thread::current().tid,
+            //     #syscall_name_str,
+            //     crate::kernel::syscall::format_expand!($($arg, $arg),*),
+            // );
+
+            retval
+        }
+
+        #(#attrs)*
+        #vis fn #real_fn(
+            thread: &crate::kernel::task::Thread,
+            trap_ctx: &mut arch::TrapContext,
+            #(#args),*
+        ) #ty_ret #body
+    }
+}
+
+/// 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:
+/// - `thread: &Thread`
+/// - `trap_ctx: &mut TrapContext`
+///
+/// The arguments of the syscall MUST implement `FromSyscallArg` trait and the return value
+/// types MUST implement `SyscallRetVal` trait.
+///
+/// # Usage
+/// ```no_run
+/// # use eonix_macros::define_syscall;
+/// #[define_syscall]
+/// fn read(fd: u32, buf: *mut u8, count: u32) -> u32
+/// {
+///     /* ... */
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn define_syscall(
+    attrs: proc_macro::TokenStream,
+    item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    define_syscall_impl(attrs.into(), item.into()).into()
+}

+ 2 - 1
src/kernel/cpu.rs

@@ -5,7 +5,8 @@ use eonix_mm::paging::Page;
 use eonix_sync::LazyLock;
 
 #[eonix_percpu::define_percpu]
-static CPU: LazyLock<CPU> = LazyLock::new(CPU::new);
+static CPU: LazyLock<CPU> =
+    LazyLock::new(|| CPU::new(unsafe { eonix_hal::trap::TRAP_STUBS_START }));
 
 /// # Safety
 /// This function is unsafe because it needs preemption to be disabled.

+ 1 - 1
src/kernel/interrupt.rs

@@ -42,7 +42,7 @@ fn fault_handler(int_stack: &mut InterruptContext) {
     }
 }
 
-#[no_mangle]
+#[eonix_hal::default_trap_handler]
 pub extern "C" fn interrupt_handler(
     int_stack: *mut InterruptContext,
     ext_ctx: *mut ExtendedContext,

+ 2 - 0
src/kernel_init.rs

@@ -220,6 +220,8 @@ extern "C" fn _init_on_new_stack(early_kernel_stack_pfn: PFN) -> ! {
 
     unsafe { init_allocator() };
 
+    eonix_hal::trap::init();
+
     kernel::interrupt::init().unwrap();
 
     kernel_init(early_kernel_stack_pfn)