Browse Source

Merge pull request #9 from SMS-Derfflinger/riscv64-support

Riscv64 support with no serial terminal support for now.
greatbridf 11 tháng trước cách đây
mục cha
commit
e5d5464c96
100 tập tin đã thay đổi với 4779 bổ sung1045 xóa
  1. 1 1
      .cargo/config.toml
  2. 49 11
      .vscode/launch.json
  3. 59 3
      .vscode/tasks.json
  4. 178 23
      Cargo.lock
  5. 6 4
      Cargo.toml
  6. 77 34
      Makefile.src
  7. 0 80
      arch/Cargo.lock
  8. 0 9
      arch/Cargo.toml
  9. 0 9
      arch/arch_macros/Cargo.toml
  10. 0 1
      arch/arch_macros/src/lib.rs
  11. 0 1
      arch/arch_macros/src/x86_64/mod.rs
  12. 0 13
      arch/src/lib.rs
  13. 0 203
      arch/src/riscv64/mm.rs
  14. 0 4
      arch/src/riscv64/mod.rs
  15. 0 34
      arch/src/x86_64/fence.rs
  16. 0 76
      arch/src/x86_64/io.rs
  17. 0 119
      arch/src/x86_64/mod.rs
  18. 27 0
      configure
  19. 9 1
      crates/eonix_hal/Cargo.toml
  20. 15 0
      crates/eonix_hal/build.rs
  21. 5 2
      crates/eonix_hal/eonix_hal_traits/src/fault.rs
  22. 13 1
      crates/eonix_hal/eonix_hal_traits/src/trap.rs
  23. 1 0
      crates/eonix_hal/src/arch/mod.rs
  24. 239 0
      crates/eonix_hal/src/arch/riscv64/bootstrap.rs
  25. 51 0
      crates/eonix_hal/src/arch/riscv64/config.rs
  26. 16 0
      crates/eonix_hal/src/arch/riscv64/console.rs
  27. 111 0
      crates/eonix_hal/src/arch/riscv64/context.rs
  28. 108 0
      crates/eonix_hal/src/arch/riscv64/cpu.rs
  29. 62 0
      crates/eonix_hal/src/arch/riscv64/fdt.rs
  30. 58 0
      crates/eonix_hal/src/arch/riscv64/fence.rs
  31. 111 0
      crates/eonix_hal/src/arch/riscv64/fpu.rs
  32. 48 0
      crates/eonix_hal/src/arch/riscv64/interrupt/mod.rs
  33. 91 0
      crates/eonix_hal/src/arch/riscv64/link.x
  34. 23 0
      crates/eonix_hal/src/arch/riscv64/memory.x
  35. 347 0
      crates/eonix_hal/src/arch/riscv64/mm.rs
  36. 12 0
      crates/eonix_hal/src/arch/riscv64/mod.rs
  37. 18 0
      crates/eonix_hal/src/arch/riscv64/time.rs
  38. 359 0
      crates/eonix_hal/src/arch/riscv64/trap/mod.rs
  39. 277 0
      crates/eonix_hal/src/arch/riscv64/trap/trap_context.rs
  40. 3 16
      crates/eonix_hal/src/arch/x86_64/bootstrap/init.rs
  41. 30 36
      crates/eonix_hal/src/arch/x86_64/context.rs
  42. 40 99
      crates/eonix_hal/src/arch/x86_64/cpu.rs
  43. 46 0
      crates/eonix_hal/src/arch/x86_64/fence.rs
  44. 0 0
      crates/eonix_hal/src/arch/x86_64/fpu.rs
  45. 3 3
      crates/eonix_hal/src/arch/x86_64/interrupt.rs
  46. 36 0
      crates/eonix_hal/src/arch/x86_64/io.rs
  47. 12 1
      crates/eonix_hal/src/arch/x86_64/link.x
  48. 7 0
      crates/eonix_hal/src/arch/x86_64/memory.x
  49. 49 2
      crates/eonix_hal/src/arch/x86_64/mm.rs
  50. 3 0
      crates/eonix_hal/src/arch/x86_64/mod.rs
  51. 2 0
      crates/eonix_hal/src/arch/x86_64/trap.rs
  52. 48 4
      crates/eonix_hal/src/arch/x86_64/trap/trap_context.rs
  53. 10 0
      crates/eonix_hal/src/bootstrap.rs
  54. 31 1
      crates/eonix_hal/src/lib.rs
  55. 7 15
      crates/eonix_hal/src/link.x.in
  56. 4 1
      crates/eonix_hal/src/mm.rs
  57. 0 1
      crates/eonix_hal/src/processor.rs
  58. 4 0
      crates/eonix_mm/src/page_table/page_table.rs
  59. 2 1
      crates/eonix_mm/src/page_table/pte_iterator.rs
  60. 0 2
      crates/eonix_percpu/eonix_percpu_macros/Cargo.toml
  61. 28 5
      crates/eonix_percpu/eonix_percpu_macros/src/lib.rs
  62. 55 0
      crates/eonix_percpu/eonix_percpu_macros/src/riscv64.rs
  63. 8 0
      crates/eonix_percpu/eonix_percpu_macros/src/x86_64.rs
  64. 6 0
      crates/eonix_percpu/src/lib.rs
  65. 0 1
      crates/eonix_runtime/Cargo.toml
  66. 3 2
      crates/eonix_runtime/src/scheduler.rs
  67. 2 1
      crates/eonix_runtime/src/task.rs
  68. 0 1
      crates/eonix_sync/eonix_sync_rt/Cargo.toml
  69. 2 0
      crates/posix_types/Cargo.toml
  70. 29 0
      crates/posix_types/src/constants.rs
  71. 55 0
      crates/posix_types/src/ctypes.rs
  72. 5 0
      crates/posix_types/src/lib.rs
  73. 121 0
      crates/posix_types/src/open.rs
  74. 13 1
      crates/posix_types/src/result.rs
  75. 7 1
      crates/posix_types/src/signal.rs
  76. 136 42
      crates/posix_types/src/signal/sig_action.rs
  77. 28 0
      crates/posix_types/src/signal/siginfo.rs
  78. 138 0
      crates/posix_types/src/signal/signal.rs
  79. 96 0
      crates/posix_types/src/stat.rs
  80. 13 0
      crates/posix_types/src/syscall_no.rs
  81. 297 0
      crates/posix_types/src/syscall_no/riscv64.rs
  82. 433 0
      crates/posix_types/src/syscall_no/x86_64.rs
  83. 13 0
      doc/mem_layout_riscv64.txt
  84. 38 23
      macros/src/lib.rs
  85. 0 1
      pretty-print.py
  86. 26 12
      script/build-img.sh
  87. 5 17
      src/driver.rs
  88. 2 1
      src/driver/ahci/control.rs
  89. 1 1
      src/driver/ahci/register.rs
  90. 40 0
      src/driver/sbi_console.rs
  91. 10 5
      src/driver/serial.rs
  92. 155 0
      src/driver/virtio.rs
  93. 34 0
      src/driver/virtio/virtio_blk.rs
  94. 22 11
      src/fs/tmpfs.rs
  95. 96 0
      src/io.rs
  96. 20 19
      src/kernel/chardev.rs
  97. 0 21
      src/kernel/constants.rs
  98. 15 8
      src/kernel/interrupt.rs
  99. 40 51
      src/kernel/mem/mm_area.rs
  100. 79 10
      src/kernel/mem/mm_list.rs

+ 1 - 1
.cargo/config.toml

@@ -1,5 +1,5 @@
 [build]
-target = 'x86_64-unknown-none.json'
+target = "riscv64gc-unknown-none-elf"
 target-dir = 'build'
 
 [unstable]

+ 49 - 11
.vscode/launch.json

@@ -3,7 +3,7 @@
         {
             "type": "cppdbg",
             "request": "launch",
-            "name": "Launch Kernel",
+            "name": "Launch Kernel (riscv64)",
             "program": "${workspaceFolder}/build/kernel.sym",
             "args": [],
             "stopAtEntry": false,
@@ -11,32 +11,65 @@
             "environment": [],
             "externalConsole": false,
             "MIMode": "gdb",
-            "miDebuggerPath": "x86_64-elf-gdb",
+            "miDebuggerPath": "/Users/david/.local/riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-apple-darwin/bin/riscv64-unknown-elf-gdb",
             "miDebuggerServerAddress": "127.0.0.1:1234",
             "setupCommands": [
+                {
+                    "text": "-enable-pretty-printing",
+                    "description": "Enable GDB pretty printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "text": "-exec set output-radix 16",
+                    "description": "Print addresses in hexadecimal",
+                    "ignoreFailures": true
+                },
                 // {
-                //     "text": "source ${env:HOME}/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/etc/gdb_load_rust_pretty_printers.py",
-                //     "description": "Load Rust pretty printers",
+                //     "text": "source ${workspaceFolder}/pretty-print.py",
+                //     "description": "Load GDB pretty printers",
                 //     "ignoreFailures": false
                 // },
+            ],
+            "preLaunchTask": "debug run riscv64",
+            "postDebugTask": "kill qemu riscv64"
+        },
+        {
+            "type": "cppdbg",
+            "request": "launch",
+            "name": "Launch Kernel (x86_64)",
+            "program": "${workspaceFolder}/build/kernel.sym",
+            "args": [],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "miDebuggerPath": "x86_64-elf-gdb",
+            "miDebuggerServerAddress": "127.0.0.1:1234",
+            "setupCommands": [
                 {
                     "text": "-enable-pretty-printing",
                     "description": "Enable GDB pretty printing",
                     "ignoreFailures": true
                 },
                 {
-                    "text": "source ${workspaceFolder}/pretty-print.py",
-                    "description": "Load GDB pretty printers",
-                    "ignoreFailures": false
+                    "text": "-exec set output-radix 16",
+                    "description": "Print addresses in hexadecimal",
+                    "ignoreFailures": true
                 },
+                // {
+                //     "text": "source ${workspaceFolder}/pretty-print.py",
+                //     "description": "Load GDB pretty printers",
+                //     "ignoreFailures": false
+                // },
             ],
-            "preLaunchTask": "debug run",
-            "postDebugTask": "kill qemu"
+            "preLaunchTask": "debug run x86_64",
+            "postDebugTask": "kill qemu x86_64"
         },
         {
             "type": "cppdbg",
             "request": "launch",
-            "name": "Attach Kernel",
+            "name": "Attach Kernel (x86_64)",
             "program": "${workspaceFolder}/build/kernel.sym",
             "args": [],
             "stopAtEntry": false,
@@ -51,7 +84,12 @@
                     "text": "-enable-pretty-printing",
                     "description": "Enable GDB pretty printing",
                     "ignoreFailures": true
-                }
+                },
+                {
+                    "text": "-exec set output-radix 16",
+                    "description": "Print addresses in hexadecimal",
+                    "ignoreFailures": true
+                },
             ]
         }
     ]

+ 59 - 3
.vscode/tasks.json

@@ -4,9 +4,9 @@
     "version": "2.0.0",
     "tasks": [
         {
-            "label": "debug run",
+            "label": "debug run riscv64",
             "type": "shell",
-            "command": "make srun",
+            "command": "make srun ARCH=riscv64",
             "isBackground": true,
             "problemMatcher": [
                 {
@@ -44,7 +44,63 @@
             }
         },
         {
-            "label": "kill qemu",
+            "label": "kill qemu riscv64",
+            "type": "shell",
+            "command": "killall qemu-system-riscv64",
+            "presentation": {
+                "echo": false,
+                "reveal": "never",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": false,
+                "clear": true
+            },
+            "group": {
+                "kind": "none",
+            }
+        },
+        {
+            "label": "debug run x86_64",
+            "type": "shell",
+            "command": "make srun ARCH=x86_64",
+            "isBackground": true,
+            "problemMatcher": [
+                {
+                    "owner": "rustc",
+                    "fileLocation": [
+                        "relative",
+                        "${workspaceFolder}"
+                    ],
+                    "pattern": {
+                        "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
+                        "file": 1,
+                        "line": 2,
+                        "column": 3,
+                        "severity": 4,
+                        "message": 5
+                    },
+                    "background": {
+                        "activeOnStart": true,
+                        "beginsPattern": "cargo build",
+                        "endsPattern": "qemu"
+                    }
+                }
+            ],
+            "presentation": {
+                "echo": false,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": false,
+                "clear": true
+            },
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
+        },
+        {
+            "label": "kill qemu x86_64",
             "type": "shell",
             "command": "killall qemu-system-x86_64",
             "presentation": {

+ 178 - 23
Cargo.lock

@@ -19,24 +19,6 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c1c330e503236d0b06386ae6cc42a513ef1ccc23c52b603c1b52f018564faf44"
 
-[[package]]
-name = "arch"
-version = "0.1.0"
-dependencies = [
- "cfg-if",
- "eonix_hal_traits",
- "eonix_mm",
-]
-
-[[package]]
-name = "arch_macros"
-version = "0.1.0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "atomic_unique_refcell"
 version = "0.1.0"
@@ -73,18 +55,48 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
 [[package]]
 name = "either"
 version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 
+[[package]]
+name = "embedded-hal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
+
+[[package]]
+name = "embedded-io"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
+
+[[package]]
+name = "enumn"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "eonix_hal"
 version = "0.1.0"
 dependencies = [
  "acpi",
- "arch",
+ "bitflags",
+ "buddy_allocator",
  "cfg-if",
  "eonix_hal_macros",
  "eonix_hal_traits",
@@ -92,6 +104,11 @@ dependencies = [
  "eonix_percpu",
  "eonix_preempt",
  "eonix_sync_base",
+ "fdt",
+ "intrusive_list",
+ "riscv 0.13.0",
+ "riscv-peripheral",
+ "sbi",
 ]
 
 [[package]]
@@ -117,7 +134,6 @@ version = "0.1.0"
 dependencies = [
  "acpi",
  "align_ext",
- "arch",
  "atomic_unique_refcell",
  "bitflags",
  "buddy_allocator",
@@ -135,6 +151,7 @@ dependencies = [
  "pointers",
  "posix_types",
  "slab_allocator",
+ "virtio-drivers",
  "xmas-elf",
 ]
 
@@ -172,7 +189,6 @@ dependencies = [
 name = "eonix_percpu_macros"
 version = "0.1.0"
 dependencies = [
- "arch_macros",
  "proc-macro2",
  "quote",
  "syn",
@@ -189,7 +205,6 @@ dependencies = [
 name = "eonix_runtime"
 version = "0.1.0"
 dependencies = [
- "arch",
  "atomic_unique_refcell",
  "eonix_hal",
  "eonix_log",
@@ -225,7 +240,6 @@ version = "0.1.0"
 name = "eonix_sync_rt"
 version = "0.1.0"
 dependencies = [
- "arch",
  "eonix_hal",
  "eonix_preempt",
  "eonix_spin",
@@ -233,6 +247,12 @@ dependencies = [
  "intrusive-collections",
 ]
 
+[[package]]
+name = "fdt"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
+
 [[package]]
 name = "intrusive-collections"
 version = "0.9.7"
@@ -270,6 +290,12 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
 [[package]]
 name = "pointers"
 version = "0.1.0"
@@ -277,6 +303,10 @@ version = "0.1.0"
 [[package]]
 name = "posix_types"
 version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+]
 
 [[package]]
 name = "proc-macro2"
@@ -296,6 +326,76 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "riscv"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afa3cdbeccae4359f6839a00e8b77e5736caa200ba216caf38d24e4c16e2b586"
+dependencies = [
+ "critical-section",
+ "embedded-hal",
+ "paste",
+ "riscv-macros",
+ "riscv-pac",
+]
+
+[[package]]
+name = "riscv"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f1671c79a01a149fe000af2429ce9ccc8e58cdecda72672355d50e5536b363c"
+dependencies = [
+ "critical-section",
+ "embedded-hal",
+ "paste",
+ "riscv-macros",
+ "riscv-pac",
+]
+
+[[package]]
+name = "riscv-macros"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8c4aa1ea1af6dcc83a61be12e8189f9b293c3ba5a487778a4cd89fb060fdbbc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "riscv-pac"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436"
+
+[[package]]
+name = "riscv-peripheral"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d50c11ddf3e2a21206642bfe6d5e06f76d5225c1fbece09952dcd40f8c49409a"
+dependencies = [
+ "embedded-hal",
+ "paste",
+ "riscv 0.14.0",
+ "riscv-pac",
+]
+
+[[package]]
+name = "safe-mmio"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db02a82ad13df46afeba34a4e54065fa912308b9101b060e4422898eac0e06f6"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "sbi"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c64a569a412de4ad7b123f429e434751d74dd7ed25654af962b93c4d1cd584e"
+
 [[package]]
 name = "slab_allocator"
 version = "0.1.0"
@@ -316,12 +416,47 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
 
+[[package]]
+name = "virtio-drivers"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe3f779fd88436e27b51540d9563c7454c8c814893a1e6f9bb6138bcac60627"
+dependencies = [
+ "bitflags",
+ "embedded-io",
+ "enumn",
+ "log",
+ "safe-mmio",
+ "thiserror",
+ "zerocopy",
+]
+
 [[package]]
 name = "xmas-elf"
 version = "0.10.0"
@@ -336,3 +471,23 @@ name = "zero"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784"
+
+[[package]]
+name = "zerocopy"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]

+ 6 - 4
Cargo.toml

@@ -7,7 +7,6 @@ edition = "2021"
 crate-type = ["bin"]
 
 [dependencies]
-arch = { path = "./arch" }
 atomic_unique_refcell = { path = "./crates/atomic_unique_refcell", features = [
     "no_std",
 ] }
@@ -32,6 +31,9 @@ acpi = "5.2.0"
 align_ext = "0.1.0"
 xmas-elf = "0.10.0"
 
+[target.'cfg(target_arch = "riscv64")'.dependencies]
+virtio-drivers = { version = "0.11.0" }
+
 [features]
 default = []
 trace_syscall = []
@@ -43,9 +45,6 @@ smp = []
 [profile.dev]
 panic = "abort"
 
-[profile.dev.package.arch]
-opt-level = 0
-
 [profile.dev.package.eonix_preempt]
 opt-level = 2
 
@@ -58,6 +57,9 @@ opt-level = 2
 [profile.dev.package.intrusive_list]
 opt-level = 2
 
+[profile.dev.package.eonix_hal]
+opt-level = 0
+
 [profile.dev.package."*"]
 opt-level = "s"
 

+ 77 - 34
Makefile.src

@@ -7,10 +7,20 @@ QEMU ?= ##QEMU##
 GDB ?= ##GDB##
 FDISK ?= ##FDISK##
 
-QEMU_ARGS ?= -machine q35 -drive id=disk,file=build/boot.img,format=raw,if=none \
-	-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 \
-	-netdev user,id=mynet0,net=192.168.1.0/24,dhcpstart=192.168.1.16 -device e1000e,netdev=mynet0 \
-	-no-reboot -no-shutdown
+COMMA := ,
+
+PROFILE = $(MODE)
+ifeq ($(MODE),debug)
+PROFILE := dev
+endif
+
+USER_PROGRAMS = $(shell find user-programs -type f)
+KERNEL_SOURCES := $(shell find src macros crates -name '*.rs' -type f)
+KERNEL_CARGO_MANIFESTS += $(shell find src macros crates -name Cargo.toml -type f)
+KERNEL_DEPS := $(KERNEL_SOURCES) $(KERNEL_CARGO_MANIFESTS)
+
+QEMU_ARGS ?= -no-reboot -no-shutdown
+CARGO_FLAGS := --profile $(PROFILE) --features $(FEATURES)$(if $(SMP),$(COMMA)smp,)
 
 ifeq ($(HOST),darwin)
 QEMU_ACCEL ?= -accel tcg
@@ -28,35 +38,64 @@ ifneq ($(SMP),)
 QEMU_ARGS += -smp $(SMP)
 endif
 
-ifeq ($(MODE),debug)
-MODE := dev
-endif
+ifeq ($(ARCH),riscv64)
 
-COMMA := ,
+BINARY_DIR_BASE := build/riscv64gc-unknown-none-elf
+BINARY_DIR := $(BINARY_DIR_BASE)/$(MODE)
+
+QEMU_ARGS += \
+	-machine virt -kernel $(BINARY_DIR)/eonix_kernel \
+	-device virtio-blk-device,drive=disk0,bus=virtio-mmio-bus.0 \
+	-device virtio-blk-device,drive=disk1,bus=virtio-mmio-bus.1 \
+	-device virtio-net-device,netdev=mynet0 \
+	-drive id=disk0,file=build/boot-riscv64.img,format=raw,if=none \
+	-drive id=disk1,file=build/fs-riscv64.img,format=raw,if=none \
+	-netdev user,id=mynet0 \
+	-rtc base=utc
+
+CARGO_FLAGS += --target riscv64gc-unknown-none-elf
+
+.PHONY: build
+build: $(BINARY_DIR)/eonix_kernel build/boot-riscv64.img build/fs-riscv64.img
+
+else ifeq ($(ARCH),x86_64)
 
-CARGO_FLAGS := --profile $(MODE) --features $(FEATURES)$(if $(SMP),$(COMMA)smp,)
+BINARY_DIR_BASE := build/x86_64-unknown-none
+BINARY_DIR := $(BINARY_DIR_BASE)/$(MODE)
+
+QEMU_ARGS += \
+	-machine q35 \
+	-device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0 \
+	-device e1000e,netdev=mynet0 \
+	-drive id=disk,file=build/boot-x86_64.img,format=raw,if=none \
+	-netdev user,id=mynet0
+
+CARGO_FLAGS += --target x86_64-unknown-none.json
+
+.PHONY: build
+build: $(BINARY_DIR)/eonix_kernel build/boot-x86_64.img
+
+endif
 
 .PHONY: run
-run: build
+run: build build/kernel.sym
 	$(QEMU) $(QEMU_ARGS) -display none -serial mon:stdio
 
 .PHONY: srun
-srun: build
+srun: build build/kernel.sym
 	$(QEMU) $(QEMU_ARGS) -display none -S -s -serial mon:stdio
 
 .PHONY: clean
 clean:
-	-mv build/fs.img .
 	-rm -rf build
 	-mkdir build
-	-mv fs.img build
 
 .PHONY: clean-all
 clean-all: clean
 	-rm Makefile
 
 .PHONY: debug
-debug:
+debug: build/kernel.sym
 	-RUST_GDB=$(GDB) rust-gdb --symbols=build/kernel.sym \
 		-iex 'source pretty-print.py' \
 		-iex 'set pagination off' \
@@ -75,34 +114,38 @@ tmux-debug:
 	-tmux attach -t gbos-debug
 	tmux kill-session -t gbos-debug
 
-.PHONY: kernel
-kernel:
+$(BINARY_DIR)/eonix_kernel: $(KERNEL_DEPS)
 	cargo build $(CARGO_FLAGS)
 
-build/kernel.sym: kernel
-	cargo objcopy $(CARGO_FLAGS) -- --only-keep-debug build/kernel.sym
+build/kernel.sym: $(BINARY_DIR)/eonix_kernel
+	cargo objcopy -q $(CARGO_FLAGS) -- --only-keep-debug build/kernel.sym
+
+build/fs-%.img: user-programs/init_script_%.sh script/build-img.sh $(USER_PROGRAMS)
+	ARCH=$* OUTPUT=$@ sh script/build-img.sh
 
-build/mbr.bin: kernel
-	cargo objcopy $(CARGO_FLAGS) -- -O binary -j .mbr build/mbr.bin
+build/mbr.bin: $(BINARY_DIR)/eonix_kernel
+	cargo objcopy -q $(CARGO_FLAGS) -- -O binary -j .mbr build/mbr.bin
 
-build/stage1.bin: kernel
-	cargo objcopy $(CARGO_FLAGS) -- -O binary -j .stage1 build/stage1.bin
+build/stage1.bin: $(BINARY_DIR)/eonix_kernel
+	cargo objcopy -q $(CARGO_FLAGS) -- -O binary -j .stage1 build/stage1.bin
 
-build/kernel.bin: kernel
-	cargo objcopy $(CARGO_FLAGS) -- -O binary --strip-debug \
+build/kernel.bin: $(BINARY_DIR)/eonix_kernel
+	cargo objcopy -q $(CARGO_FLAGS) -- -O binary --strip-debug \
 		-R .mbr -R .stage1 build/kernel.bin
 
-build/fs.img: init_script.sh
-	sh script/build-img.sh
+build/boot-x86_64.img: build/fs-x86_64.img build/mbr.bin build/stage1.bin build/kernel.bin
+	dd if=build/mbr.bin of=$@ bs=512 count=1 conv=notrunc 2> /dev/null
+	dd if=build/stage1.bin of=$@ bs=512 seek=1 conv=notrunc 2> /dev/null
+	dd if=build/kernel.bin of=$@ bs=4096 seek=1 conv=notrunc 2> /dev/null
+	dd if=$< of=$@ bs=$(shell expr 4 \* 1024 \* 1024) \
+		seek=1 conv=notrunc 2> /dev/null
+	sh -c 'echo n; echo; echo; echo 8192; echo; echo a; echo w' \
+		| $(FDISK) $@ 2> /dev/null > /dev/null
 
-build/boot.img: build/mbr.bin build/stage1.bin build/kernel.bin build/fs.img
-	dd if=build/mbr.bin of=build/boot.img bs=512 count=1 conv=notrunc 2> /dev/null
-	dd if=build/stage1.bin of=build/boot.img bs=512 seek=1 conv=notrunc 2> /dev/null
-	dd if=build/kernel.bin of=build/boot.img bs=4096 seek=1 conv=notrunc 2> /dev/null
-	dd if=build/fs.img of=build/boot.img bs=$(shell expr 4 \* 1024 \* 1024) \
+build/boot-riscv64.img: build/fs-riscv64.img
+	dd if=$< of=$@ bs=$(shell expr 4 \* 1024 \* 1024) \
 		seek=1 conv=notrunc 2> /dev/null
 	sh -c 'echo n; echo; echo; echo 8192; echo; echo a; echo w' \
-		| $(FDISK) build/boot.img 2> /dev/null > /dev/null
+		| $(FDISK) $@ 2> /dev/null > /dev/null
 
-.PHONY: build
-build: build/boot.img build/kernel.sym
+.DEFAULT_GOAL := build

+ 0 - 80
arch/Cargo.lock

@@ -1,80 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "arch"
-version = "0.1.0"
-dependencies = [
- "bitflags",
- "cfg-if",
- "eonix_mm",
- "percpu-macros",
-]
-
-[[package]]
-name = "bitflags"
-version = "2.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "eonix_mm"
-version = "0.1.0"
-dependencies = [
- "intrusive_list",
-]
-
-[[package]]
-name = "intrusive_list"
-version = "0.1.0"
-
-[[package]]
-name = "percpu-macros"
-version = "0.1.0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.89"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"

+ 0 - 9
arch/Cargo.toml

@@ -1,9 +0,0 @@
-[package]
-name = "arch"
-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"

+ 0 - 9
arch/arch_macros/Cargo.toml

@@ -1,9 +0,0 @@
-[package]
-name = "arch_macros"
-version = "0.1.0"
-edition = "2024"
-
-[dependencies]
-proc-macro2 = "1.0"
-quote = "1.0"
-syn = { version = "2.0", features = ["full"] }

+ 0 - 1
arch/arch_macros/src/lib.rs

@@ -1 +0,0 @@
-pub mod x86_64;

+ 0 - 1
arch/arch_macros/src/x86_64/mod.rs

@@ -1 +0,0 @@
-pub mod percpu;

+ 0 - 13
arch/src/lib.rs

@@ -1,13 +0,0 @@
-#![no_std]
-
-cfg_if::cfg_if! {
-    if #[cfg(target_arch = "x86_64")] {
-        mod x86_64;
-        pub use self::x86_64::*;
-    } else if #[cfg(target_arch = "riscv64")] {
-        mod riscv;
-        pub use self::riscv::*;
-    } else if #[cfg(target_arch = "aarch64")]{
-        // TODO!!!
-    }
-}

+ 0 - 203
arch/src/riscv64/mm.rs

@@ -1,203 +0,0 @@
-use core::{marker::PhantomData, ptr::NonNull};
-use eonix_mm::{
-    address::{Addr as _, PAddr},
-    page_table::{PageAttribute, PageTableLevel, PagingMode, RawPageTable, PTE},
-    paging::{PageBlock, PFN},
-};
-
-pub const PAGE_SIZE: usize = 0x1000;
-const PAGE_TABLE_BASE: PFN = PFN::from_val(0x8030_0000 >> 12);
-
-const PA_V: u64 = 0b1 << 0;
-const PA_R: u64 = 0b1 << 1;
-const PA_W: u64 = 0b1 << 2;
-const PA_X: u64 = 0b1 << 3;
-const PA_U: u64 = 0b1 << 4;
-const PA_G: u64 = 0b1 << 5;
-const PA_A: u64 = 0b1 << 6;
-const PA_D: u64 = 0b1 << 7;
-
-// in RSW
-const PA_COW: u64 = 0b1 << 8;
-const PA_MMAP: u64 = 0b1 << 9;
-
-const PA_SHIFT: u64 = 10;
-const PA_MASK: u64 = 0xFFC0_0000_0000_03FF; // 44 bit PPN, from 10 to 53
-// Bit 0-9 (V, R, W, X, U, G, A, D, RSW)
-const PA_FLAGS_MASK: u64 = 0x3FF; // 0b11_1111_1111
-
-
-
-#[repr(transparent)]
-pub struct PTE64(u64);
-
-#[derive(Clone, Copy)]
-pub struct PageAttribute64(u64);
-
-pub struct RawPageTableSv39<'a>(NonNull<PTE64>, PhantomData<&'a ()>);
-
-pub struct PagingModeSv39;
-
-impl PTE for PTE64 {
-    type Attr = PageAttribute64;
-
-    fn set(&mut self, pfn: PFN, attr: Self::Attr) {
-        self.0 = (PAddr::from(pfn).addr() as u64 & !PA_MASK) | (attr.0 & PA_MASK);
-    }
-
-    fn get(&self) -> (PFN, Self::Attr) {
-        (
-            PFN::from(PAddr::from((self.0 & !PA_MASK) as usize)),
-            PageAttribute64(self.0 & PA_MASK),
-        )
-    }
-
-    fn take(&mut self) -> (PFN, Self::Attr) {
-        let pfn_attr = self.get();
-        self.0 = 0;
-        pfn_attr
-    }
-}
-
-impl PagingMode for PagingModeSv39 {
-    type Entry = PTE64;
-    type RawTable<'a> = RawPageTableSv39<'a>;
-    const LEVELS: &'static [PageTableLevel] = &[
-        PageTableLevel::new(30, 9),
-        PageTableLevel::new(21, 9),
-        PageTableLevel::new(12, 9),
-    ];
-    const KERNEL_ROOT_TABLE_PFN: PFN = PAGE_TABLE_BASE;
-}
-
-impl<'a> RawPageTable<'a> for RawPageTableSv39<'a> {
-    type Entry = PTE64;
-
-    fn index(&self, index: u16) -> &'a Self::Entry {
-        unsafe { &self.0.cast::<[PTE64; 512]>().as_ref()[index as usize] }
-    }
-
-    fn index_mut(&mut self, index: u16) -> &'a mut Self::Entry {
-        unsafe { &mut self.0.cast::<[PTE64; 512]>().as_mut()[index as usize] }
-    }
-
-    unsafe fn from_ptr(ptr: NonNull<PageBlock>) -> Self {
-        Self(ptr.cast(), PhantomData)
-    }
-}
-
-impl PageAttribute for PageAttribute64 {
-    fn new() -> Self {
-        Self(PA_R)
-    }
-
-    fn present(self, present: bool) -> Self {
-        if present {
-            Self(self.0 | PA_V)
-        } else {
-            Self(self.0 & !PA_V)
-        }
-    }
-
-    fn write(self, write: bool) -> Self {
-        if write {
-            Self(self.0 | PA_W)
-        } else {
-            Self(self.0 & !PA_W)
-        }
-    }
-
-    fn execute(self, execute: bool) -> Self {
-        if execute {
-            Self(self.0 | PA_X)
-        } else {
-            Self(self.0 & !PA_X)
-        }
-    }
-
-    fn user(self, user: bool) -> Self {
-        if user {
-            Self(self.0 | PA_U)
-        } else {
-            Self(self.0 & !PA_U)
-        }
-    }
-
-    fn accessed(self, accessed: bool) -> Self {
-        if accessed {
-            Self(self.0 | PA_A)
-        } else {
-            Self(self.0 & !PA_A)
-        }
-    }
-
-    fn dirty(self, dirty: bool) -> Self {
-        if dirty {
-            Self(self.0 | PA_D)
-        } else {
-            Self(self.0 & !PA_D)
-        }
-    }
-
-    fn global(self, global: bool) -> Self {
-        if global {
-            Self(self.0 | PA_G)
-        } else {
-            Self(self.0 & !PA_G)
-        }
-    }
-
-    fn copy_on_write(self, cow: bool) -> Self {
-        if cow {
-            Self(self.0 | PA_COW)
-        } else {
-            Self(self.0 & !PA_COW)
-        }
-    }
-
-    fn mapped(self, mmap: bool) -> Self {
-        if mmap {
-            Self(self.0 | PA_MMAP)
-        } else {
-            Self(self.0 & !PA_MMAP)
-        }
-    }
-
-    fn is_present(&self) -> bool {
-        self.0 & PA_V != 0
-    }
-
-    fn is_write(&self) -> bool {
-        self.0 & PA_W != 0
-    }
-
-    fn is_execute(&self) -> bool {
-        self.0 & PA_X != 0
-    }
-
-    fn is_user(&self) -> bool {
-        self.0 & PA_U != 0
-    }
-
-    fn is_accessed(&self) -> bool {
-        self.0 & PA_A != 0
-    }
-
-    fn is_dirty(&self) -> bool {
-        self.0 & PA_D != 0
-    }
-
-    fn is_global(&self) -> bool {
-        self.0 & PA_G != 0
-    }
-
-    fn is_copy_on_write(&self) -> bool {
-        self.0 & PA_COW != 0
-    }
-
-    fn is_mapped(&self) -> bool {
-        self.0 & PA_MMAP != 0
-    }
-}
-
-pub type DefaultPagingMode = PagingModeSv39;

+ 0 - 4
arch/src/riscv64/mod.rs

@@ -1,4 +0,0 @@
-mod start;
-mod mm;
-
-pub use self::start::*;

+ 0 - 34
arch/src/x86_64/fence.rs

@@ -1,34 +0,0 @@
-use core::arch::asm;
-
-#[doc(hidden)]
-/// Issues a full memory barrier.
-///
-/// Note that this acts as a low-level operation **ONLY** and should be used with caution.
-/// **NO COMPILER BARRIERS** are emitted by this function.
-pub fn memory_barrier() {
-    unsafe {
-        asm!("mfence", options(nostack, nomem, preserves_flags));
-    }
-}
-
-#[doc(hidden)]
-/// Issues a read memory barrier.
-///
-/// Note that this acts as a low-level operation **ONLY** and should be used with caution.
-/// **NO COMPILER BARRIERS** are emitted by this function.
-pub fn read_memory_barrier() {
-    unsafe {
-        asm!("lfence", options(nostack, nomem, preserves_flags));
-    }
-}
-
-#[doc(hidden)]
-/// Issues a write memory barrier.
-///
-/// Note that this acts as a low-level operation **ONLY** and should be used with caution.
-/// **NO COMPILER BARRIERS** are emitted by this function.
-pub fn write_memory_barrier() {
-    unsafe {
-        asm!("sfence", options(nostack, nomem, preserves_flags));
-    }
-}

+ 0 - 76
arch/src/x86_64/io.rs

@@ -1,76 +0,0 @@
-use core::arch::asm;
-
-pub fn inb(no: u16) -> u8 {
-    let data;
-    unsafe {
-        asm!(
-            "inb %dx, %al",
-            in("dx") no,
-            out("al") data,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-
-    data
-}
-
-pub fn inw(no: u16) -> u16 {
-    let data;
-    unsafe {
-        asm!(
-            "inw %dx, %ax",
-            in("dx") no,
-            out("ax") data,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-
-    data
-}
-
-pub fn inl(no: u16) -> u32 {
-    let data;
-    unsafe {
-        asm!(
-            "inl %dx, %eax",
-            in("dx") no,
-            out("eax") data,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-
-    data
-}
-
-pub fn outb(no: u16, data: u8) {
-    unsafe {
-        asm!(
-            "outb %al, %dx",
-            in("al") data,
-            in("dx") no,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-}
-
-pub fn outw(no: u16, data: u16) {
-    unsafe {
-        asm!(
-            "outw %ax, %dx",
-            in("ax") data,
-            in("dx") no,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-}
-
-pub fn outl(no: u16, data: u32) {
-    unsafe {
-        asm!(
-            "outl %eax, %dx",
-            in("eax") data,
-            in("dx") no,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-}

+ 0 - 119
arch/src/x86_64/mod.rs

@@ -1,119 +0,0 @@
-mod fence;
-mod fpu;
-mod io;
-
-use core::arch::asm;
-use eonix_mm::address::{Addr as _, PAddr, VAddr};
-use eonix_mm::paging::PFN;
-
-pub use self::io::*;
-pub use fence::*;
-pub use fpu::*;
-
-#[inline(always)]
-pub fn flush_tlb(vaddr: usize) {
-    unsafe {
-        asm!(
-            "invlpg ({})",
-            in(reg) vaddr,
-            options(att_syntax)
-        );
-    }
-}
-
-#[inline(always)]
-pub fn flush_tlb_all() {
-    unsafe {
-        asm!(
-            "mov %cr3, %rax",
-            "mov %rax, %cr3",
-            out("rax") _,
-            options(att_syntax)
-        );
-    }
-}
-
-#[inline(always)]
-pub fn get_root_page_table_pfn() -> PFN {
-    let cr3: usize;
-    unsafe {
-        asm!(
-            "mov %cr3, {0}",
-            out(reg) cr3,
-            options(att_syntax)
-        );
-    }
-    PFN::from(PAddr::from(cr3))
-}
-
-#[inline(always)]
-pub fn set_root_page_table_pfn(pfn: PFN) {
-    unsafe {
-        asm!(
-            "mov {0}, %cr3",
-            in(reg) PAddr::from(pfn).addr(),
-            options(att_syntax)
-        );
-    }
-}
-
-#[inline(always)]
-pub fn get_page_fault_address() -> VAddr {
-    let cr2: usize;
-    unsafe {
-        asm!(
-            "mov %cr2, {}",
-            out(reg) cr2,
-            options(att_syntax)
-        );
-    }
-    VAddr::from(cr2)
-}
-
-#[inline(always)]
-pub fn halt() {
-    unsafe {
-        asm!("hlt", options(att_syntax, nostack));
-    }
-}
-
-#[inline(always)]
-pub fn pause() {
-    unsafe {
-        asm!("pause", options(att_syntax, nostack));
-    }
-}
-
-#[inline(always)]
-pub fn rdmsr(msr: u32) -> u64 {
-    let edx: u32;
-    let eax: u32;
-
-    unsafe {
-        asm!(
-            "rdmsr",
-            in("ecx") msr,
-            out("eax") eax,
-            out("edx") edx,
-            options(att_syntax),
-        );
-    }
-
-    (edx as u64) << 32 | eax as u64
-}
-
-#[inline(always)]
-pub fn wrmsr(msr: u32, value: u64) {
-    let eax = value as u32;
-    let edx = (value >> 32) as u32;
-
-    unsafe {
-        asm!(
-            "wrmsr",
-            in("ecx") msr,
-            in("eax") eax,
-            in("edx") edx,
-            options(att_syntax),
-        );
-    }
-}

+ 27 - 0
configure

@@ -5,6 +5,33 @@ event() {
     printf "$1... "
 }
 
+ARCH=${ARCH:-x86_64}
+
+# Define toolchain and QEMU/GDB settings for per architecture
+event "target architecture"
+echo "$ARCH"
+case "$ARCH" in
+    x86_64)
+        QEMU_EXECUTABLES="qemu-system-x86_64"
+        GDB_EXECUTABLES="gdb x86_64-elf-gdb"
+        : "${CROSS_COMPILE:=}"
+        ;;
+    riscv64)
+        QEMU_EXECUTABLES="qemu-system-riscv64"
+        GDB_EXECUTABLES="gdb riscv64-unknown-elf-gdb"
+        : "${CROSS_COMPILE:=riscv64-unknown-elf-}"
+        ;;
+    aarch64)
+        QEMU_EXECUTABLES="qemu-system-aarch64"
+        GDB_EXECUTABLES="gdb aarch64-none-elf-gdb"
+        : "${CROSS_COMPILE:=aarch64-none-elf-}"
+        ;;
+    *)
+        echo "Unsupported ARCH: $ARCH"
+        exit 1
+        ;;
+esac
+
 if [ "$QEMU" = "" ]; then
     event "checking default qemu"
     QEMU="qemu-system-$DEFAULT_ARCH"

+ 9 - 1
crates/eonix_hal/Cargo.toml

@@ -8,7 +8,6 @@ links = "eonix_hal"
 eonix_hal_traits = { path = "./eonix_hal_traits" }
 eonix_hal_macros = { path = "./eonix_hal_macros" }
 
-arch = { path = "../../arch" }
 eonix_mm = { path = "../eonix_mm" }
 eonix_sync_base = { path = "../eonix_sync/eonix_sync_base" }
 eonix_percpu = { path = "../eonix_percpu" }
@@ -16,3 +15,12 @@ eonix_preempt = { path = "../eonix_preempt" }
 
 acpi = "5.2.0"
 cfg-if = "1.0"
+
+[target.'cfg(target_arch = "riscv64")'.dependencies]
+intrusive_list = { path = "../intrusive_list" }
+buddy_allocator = { path = "../buddy_allocator" }
+sbi = "0.3.0"
+riscv = { version = "0.13.0", features = ["s-mode"] }
+riscv-peripheral = { version = "0.3.0" }
+fdt = "0.1"
+bitflags = "2.6.0"

+ 15 - 0
crates/eonix_hal/build.rs

@@ -7,6 +7,18 @@ fn read_dependent_script(script: &str) -> Result<String, Box<dyn std::error::Err
     Ok(content)
 }
 
+fn process_ldscript_riscv64(script: &mut String) -> Result<(), Box<dyn std::error::Error>> {
+    println!("cargo:extra-link-args= --no-check-sections");
+
+    let memory = read_dependent_script("src/arch/riscv64/memory.x")?;
+    let link = read_dependent_script("src/arch/riscv64/link.x")?;
+
+    *script = memory + script;
+    script.push_str(&link);
+
+    Ok(())
+}
+
 fn process_ldscript_x86(script: &mut String) -> Result<(), Box<dyn std::error::Error>> {
     // Otherwise `bootstrap.rs` might be ignored and not linked in.
     println!("cargo:extra-link-args=--undefined=move_mbr --no-check-sections");
@@ -28,6 +40,9 @@ fn process_ldscript_arch(
         "x86_64" => {
             process_ldscript_x86(script)?;
         }
+        "riscv64" => {
+            process_ldscript_riscv64(script)?;
+        }
         _ => panic!("Unsupported architecture: {}", arch),
     }
 

+ 5 - 2
crates/eonix_hal/eonix_hal_traits/src/fault.rs

@@ -1,9 +1,9 @@
 use bitflags::bitflags;
+use eonix_mm::address::VAddr;
 
 bitflags! {
     #[derive(Debug)]
     pub struct PageFaultErrorCode: u32 {
-        const NonPresent = 1;
         const Read = 2;
         const Write = 4;
         const InstructionFetch = 8;
@@ -15,6 +15,9 @@ bitflags! {
 pub enum Fault {
     InvalidOp,
     BadAccess,
-    PageFault(PageFaultErrorCode),
+    PageFault {
+        error_code: PageFaultErrorCode,
+        address: VAddr,
+    },
     Unknown(usize),
 }

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

@@ -1,5 +1,6 @@
-use crate::fault::Fault;
+use crate::{context::RawTaskContext, fault::Fault};
 use core::marker::PhantomData;
+use eonix_mm::address::VAddr;
 
 /// A raw trap context.
 ///
@@ -24,10 +25,21 @@ pub trait RawTrapContext: Copy {
     fn set_user_mode(&mut self, user: bool);
 
     fn set_user_return_value(&mut self, retval: usize);
+
+    fn set_user_call_frame<E>(
+        &mut self,
+        pc: usize,
+        sp: Option<usize>,
+        ra: Option<usize>,
+        args: &[usize],
+        write_memory: impl Fn(VAddr, &[u8]) -> Result<(), E>,
+    ) -> Result<(), E>;
 }
 
 #[doc(notable_trait)]
 pub trait TrapReturn {
+    type TaskContext: RawTaskContext;
+
     /// Return to the context before the trap occurred.
     ///
     /// # Safety

+ 1 - 0
crates/eonix_hal/src/arch/mod.rs

@@ -1,3 +1,4 @@
+#![allow(warnings)]
 cfg_if::cfg_if! {
     if #[cfg(target_arch = "x86_64")] {
         mod x86_64;

+ 239 - 0
crates/eonix_hal/src/arch/riscv64/bootstrap.rs

@@ -0,0 +1,239 @@
+use super::{
+    config::{self, mm::*},
+    console::write_str,
+    trap::TRAP_SCRATCH,
+};
+use crate::{
+    arch::{
+        cpu::CPU,
+        fdt::{init_dtb_and_fdt, FdtExt},
+        mm::{ArchPhysAccess, FreeRam, PageAttribute64, GLOBAL_PAGE_TABLE},
+        interrupt::enable_timer_interrupt,
+    },
+    bootstrap::BootStrapData,
+    mm::{ArchMemory, ArchPagingMode, BasicPageAlloc, BasicPageAllocRef, ScopedAllocator},
+};
+use core::arch::naked_asm;
+use core::{
+    alloc::Allocator,
+    arch::asm,
+    cell::RefCell,
+    sync::atomic::{AtomicBool, AtomicUsize},
+};
+use eonix_hal_traits::mm::Memory;
+use eonix_mm::{
+    address::{Addr as _, PAddr, PRange, PhysAccess, VAddr, VRange},
+    page_table::{PageAttribute, PagingMode, PTE as _},
+    paging::{Page, PageAccess, PageAlloc, PAGE_SIZE, PFN},
+};
+use eonix_percpu::PercpuArea;
+use fdt::Fdt;
+use riscv::{asm::sfence_vma_all, register::satp};
+use sbi::legacy::console_putchar;
+
+#[unsafe(link_section = ".bootstrap.stack")]
+static BOOT_STACK: [u8; 4096 * 16] = [0; 4096 * 16];
+
+static BOOT_STACK_START: &'static [u8; 4096 * 16] = &BOOT_STACK;
+
+#[repr(C, align(4096))]
+struct PageTable([u64; PTES_PER_PAGE]);
+
+/// map 0x8000 0000 to itself and 0xffff ffff 8000 0000
+#[unsafe(link_section = ".bootstrap.page_table.1")]
+static BOOT_PAGE_TABLE: PageTable = {
+    let mut arr: [u64; PTES_PER_PAGE] = [0; PTES_PER_PAGE];
+    arr[0] = 0 | 0x2f;
+    arr[510] = 0 | 0x2f;
+    arr[511] = (0x80202 << 10) | 0x21;
+
+    PageTable(arr)
+};
+
+#[unsafe(link_section = ".bootstrap.page_table.2")]
+#[used]
+static PT1: PageTable = {
+    let mut arr: [u64; PTES_PER_PAGE] = [0; PTES_PER_PAGE];
+    arr[510] = (0x80000 << 10) | 0x2f;
+
+    PageTable(arr)
+};
+
+/// bootstrap in rust
+#[unsafe(naked)]
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".bootstrap.entry")]
+unsafe extern "C" fn _start(hart_id: usize, dtb_addr: usize) -> ! {
+    naked_asm!(
+        "
+            ld    sp, 2f
+            li    t0, 0x10000
+            add   sp, sp, t0
+            ld    t0, 3f
+            srli  t0, t0, 12
+            li    t1, 9 << 60
+            or    t0, t0, t1
+            csrw  satp, t0
+            sfence.vma
+            ld    t0, 4f
+            jalr  t0                      // call riscv64_start
+
+            .pushsection .bootstrap.data, \"aw\", @progbits
+            2:
+            .8byte {boot_stack}
+            3:
+            .8byte {page_table}
+            4:
+            .8byte {riscv64_start}
+            .popsection
+        ",
+        boot_stack = sym BOOT_STACK,
+        page_table = sym BOOT_PAGE_TABLE,
+        riscv64_start = sym riscv64_start,
+    )
+}
+
+/// TODO:
+/// 启动所有的cpu
+pub unsafe extern "C" fn riscv64_start(hart_id: usize, dtb_addr: PAddr) -> ! {
+    let fdt = Fdt::from_ptr(ArchPhysAccess::as_ptr(dtb_addr).as_ptr())
+        .expect("Failed to parse DTB from static memory.");
+
+    let real_allocator = RefCell::new(BasicPageAlloc::new());
+    let alloc = BasicPageAllocRef::new(&real_allocator);
+
+    for range in fdt.present_ram().free_ram() {
+        real_allocator.borrow_mut().add_range(range);
+    }
+
+    setup_kernel_page_table(&alloc);
+    unsafe {
+        init_dtb_and_fdt(dtb_addr);
+    }
+
+    setup_cpu(&alloc, hart_id);
+
+    // TODO: set up interrupt, smp
+    ScopedAllocator::new(&mut [0; 1024])
+        .with_alloc(|mem_alloc| bootstrap_smp(mem_alloc, &real_allocator));
+
+    unsafe extern "Rust" {
+        fn _eonix_hal_main(_: BootStrapData) -> !;
+    }
+
+    let start = unsafe {
+        ((&BOOT_STACK_START) as *const &'static [u8; 4096 * 16]).read_volatile() as *const _
+            as usize
+    };
+    let bootstrap_data = BootStrapData {
+        early_stack: PRange::new(PAddr::from(start), PAddr::from(start + 4096 * 16)),
+        allocator: Some(real_allocator),
+    };
+
+    unsafe {
+        _eonix_hal_main(bootstrap_data);
+    }
+}
+
+unsafe extern "C" {
+    fn BSS_LENGTH();
+    fn KIMAGE_PAGES();
+}
+
+/// TODO:
+/// 对kernel image添加更细的控制,或者不加也行
+fn setup_kernel_page_table(alloc: impl PageAlloc) {
+    let global_page_table = &GLOBAL_PAGE_TABLE;
+
+    let attr = PageAttribute::WRITE
+        | PageAttribute::READ
+        | PageAttribute::EXECUTE
+        | PageAttribute::GLOBAL
+        | PageAttribute::PRESENT;
+
+    const KERNEL_BSS_START: VAddr = VAddr::from(0xffffffff40000000);
+
+    // Map kernel BSS
+    for pte in global_page_table.iter_kernel_in(
+        VRange::from(KERNEL_BSS_START).grow(BSS_LENGTH as usize),
+        ArchPagingMode::LEVELS,
+        &alloc,
+    ) {
+        let page = Page::alloc_in(&alloc);
+
+        let attr = {
+            let mut attr = attr.clone();
+            attr.remove(PageAttribute::EXECUTE);
+            attr
+        };
+        pte.set(page.into_raw(), attr.into());
+    }
+
+    sfence_vma_all();
+
+    unsafe {
+        core::ptr::write_bytes(KERNEL_BSS_START.addr() as *mut (), 0, BSS_LENGTH as usize);
+    }
+
+    unsafe {
+        satp::set(
+            satp::Mode::Sv48,
+            0,
+            usize::from(PFN::from(global_page_table.addr())),
+        );
+    }
+    sfence_vma_all();
+}
+
+/// set up tp register to percpu
+fn setup_cpu(alloc: impl PageAlloc, hart_id: usize) {
+    let mut percpu_area = PercpuArea::new(|layout| {
+        let page_count = layout.size().div_ceil(PAGE_SIZE);
+        let page = Page::alloc_at_least_in(page_count, alloc);
+
+        let ptr = ArchPhysAccess::get_ptr_for_page(&page).cast();
+        page.into_raw();
+
+        ptr
+    });
+
+    // set tp(x4) register
+    percpu_area.setup(|pointer| {
+        let percpu_base_addr = pointer.addr().get();
+        unsafe {
+            asm!(
+                "mv tp, {0}",
+                in(reg) percpu_base_addr,
+                options(nostack, preserves_flags)
+            );
+        }
+    });
+
+    let mut cpu = CPU::local();
+    unsafe {
+        cpu.as_mut().init(hart_id);
+    }
+
+    percpu_area.register(cpu.cpuid());
+
+    unsafe {
+        // SAFETY: Interrupts are disabled.
+        TRAP_SCRATCH
+            .as_mut()
+            .set_kernel_tp(PercpuArea::get_for(cpu.cpuid()).unwrap().cast());
+    }
+
+    // set current hart's mtimecmp register
+    enable_timer_interrupt();
+}
+
+/// TODO
+fn bootstrap_smp(alloc: impl Allocator, page_alloc: &RefCell<BasicPageAlloc>) {}
+
+pub fn early_console_write(s: &str) {
+    write_str(s);
+}
+
+pub fn early_console_putchar(ch: u8) {
+    console_putchar(ch);
+}

+ 51 - 0
crates/eonix_hal/src/arch/riscv64/config.rs

@@ -0,0 +1,51 @@
+/// mm
+pub mod mm {
+    pub const ROOT_PAGE_TABLE_PHYS_ADDR: usize = 0x8020_1000;
+    pub const PHYS_MAP_VIRT: usize = 0xffff_ff00_0000_0000;
+    pub const KIMAGE_PHYS_BASE: usize = 0x8020_0000;
+    pub const KIMAGE_OFFSET: usize = 0xffff_ffff_0000_0000;
+    pub const MMIO_VIRT_BASE: usize = KIMAGE_OFFSET;
+    pub const KIMAGE_VIRT_BASE: usize = KIMAGE_OFFSET + KIMAGE_PHYS_BASE;
+    pub const PAGE_SIZE: usize = 1 << PAGE_SIZE_BITS;
+    pub const PAGE_SIZE_BITS: usize = 12;
+    // 128GB
+    pub const MEMORY_SIZE: usize = 0x20_0000_0000;
+
+    pub const PTE_SIZE: usize = 8;
+    pub const PTES_PER_PAGE: usize = PAGE_SIZE / PTE_SIZE;
+    pub const ROOT_PAGE_TABLE_PFN: usize = ROOT_PAGE_TABLE_PHYS_ADDR >> 12;
+    pub const PAGE_TABLE_PHYS_END: usize = 0x8080_0000;
+}
+
+pub mod platform {
+    pub mod virt {
+        pub const PLIC_BASE: usize = 0x0C00_0000;
+
+        pub const PLIC_ENABLE_PER_HART_OFFSET: usize = 0x80; // 每个 Hart使能块 0x80 字节 (128 字节)
+
+        pub const PLIC_THRESHOLD_CLAIM_COMPLETE_PER_HART_OFFSET: usize = 0x1000; // 每个 Hart/上下文块 0x1000 字节 (4KB)
+
+        pub const PLIC_PRIORITY_OFFSET: usize = 0x0000_0000;
+        pub const PLIC_PENDING_OFFSET: usize = 0x0000_1000;
+        pub const PLIC_ENABLE_OFFSET: usize = 0x0000_2000; // Varies by context and mode (M/S/U)
+        pub const PLIC_THRESHOLD_OFFSET: usize = 0x0020_0000; // Varies by context and mode (M/S/U)
+        pub const PLIC_CLAIM_COMPLETE_OFFSET: usize = 0x0020_0004; // Varies by context and mode (M/S/U)
+
+        // PLIC Context IDs for S-mode (assuming hart 0's S-mode context is 1, hart 1's is 3, etc.)
+        // A common pattern is: context_id = hart_id * 2 + 1 (for S-mode)
+        pub const PLIC_S_MODE_CONTEXT_STRIDE: usize = 2;
+
+        // CLINT (Core Local Interruptor) memory-mapped registers
+        // Base address for CLINT on QEMU virt platform
+        pub const CLINT_BASE: usize = 0x200_0000;
+        pub const CLINT_MSIP_OFFSET: usize = 0x0000; // Machine-mode Software Interrupt Pending (MSIP)
+        pub const CLINT_MTIMECMP_OFFSET: usize = 0x4000; // Machine-mode Timer Compare (MTIMECMP)
+        pub const CLINT_MTIME_OFFSET: usize = 0xBFF8;
+        // TODO: this should get in fdt
+        pub const CPU_FREQ_HZ: u64 = 10_000_000;
+    }
+}
+
+pub mod time {
+    pub const INTERRUPTS_PER_SECOND: usize = 100;
+}

+ 16 - 0
crates/eonix_hal/src/arch/riscv64/console.rs

@@ -0,0 +1,16 @@
+use sbi::legacy::{console_putchar, console_getchar};
+
+pub fn getchar() -> Option<u8> {
+    let c = console_getchar();
+    if c == Some(!0) {
+        None
+    } else {
+        c
+    }
+}
+
+pub fn write_str(s: &str) {
+    for c in s.chars() {
+        console_putchar(c as u8);
+    }
+}

+ 111 - 0
crates/eonix_hal/src/arch/riscv64/context.rs

@@ -0,0 +1,111 @@
+use core::arch::naked_asm;
+use eonix_hal_traits::context::RawTaskContext;
+use riscv::register::sstatus::Sstatus;
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct TaskContext {
+    // s0-11
+    s: [u64; 12],
+    sp: u64,
+    ra: u64,
+    sstatus: Sstatus,
+}
+
+impl RawTaskContext for TaskContext {
+    fn new() -> Self {
+        Self::new()
+    }
+
+    fn set_program_counter(&mut self, pc: usize) {
+        self.ra = pc as u64;
+    }
+
+    fn set_stack_pointer(&mut self, sp: usize) {
+        self.sp = sp as u64;
+    }
+
+    fn is_interrupt_enabled(&self) -> bool {
+        self.sstatus.sie()
+    }
+
+    fn set_interrupt_enabled(&mut self, is_enabled: bool) {
+        self.sstatus.set_sie(is_enabled);
+    }
+
+    fn call(&mut self, func: unsafe extern "C" fn(usize) -> !, arg: usize) {
+        self.s[0] = func as u64;
+        self.s[1] = arg as u64;
+
+        self.set_program_counter(Self::do_call as usize);
+    }
+
+    #[unsafe(naked)]
+    unsafe extern "C" fn switch(from: &mut Self, to: &mut Self) {
+        // Input arguments `from` and `to` will be in `a0` (x10) and `a1` (x11).
+        naked_asm!(
+            // Save current task's callee-saved registers to `from` context
+            "sd   s0, 0(a0)",
+            "sd   s1, 8(a0)",
+            "sd   s2, 16(a0)",
+            "sd   s3, 24(a0)",
+            "sd   s4, 32(a0)",
+            "sd   s5, 40(a0)",
+            "sd   s6, 48(a0)",
+            "sd   s7, 56(a0)",
+            "sd   s8, 64(a0)",
+            "sd   s9, 72(a0)",
+            "sd  s10, 80(a0)",
+            "sd  s11, 88(a0)",
+            "sd   sp, 96(a0)",
+            "sd   ra, 104(a0)",
+            "csrr t0, sstatus",
+            "sd   t0, 112(a0)",
+            "",
+            "ld   s0, 0(a1)",
+            "ld   s1, 8(a1)",
+            "ld   s2, 16(a1)",
+            "ld   s3, 24(a1)",
+            "ld   s4, 32(a1)",
+            "ld   s5, 40(a1)",
+            "ld   s6, 48(a1)",
+            "ld   s7, 56(a1)",
+            "ld   s8, 64(a1)",
+            "ld   s9, 72(a1)",
+            "ld  s10, 80(a1)",
+            "ld  s11, 88(a1)",
+            "ld   sp, 96(a1)",
+            "ld   ra, 104(a1)",
+            "ld   t0, 112(a1)",
+            "csrw sstatus, t0",
+            "ret",
+        );
+    }
+}
+
+impl TaskContext {
+    pub const fn new() -> Self {
+        Self {
+            s: [0; 12],
+            sp: 0,
+            ra: 0,
+            sstatus: Sstatus::from_bits((1 << 13) | (1 << 18)), // FS = Initial, SUM = 1.
+        }
+    }
+
+    #[unsafe(naked)]
+    /// Maximum of 5 arguments supported.
+    unsafe extern "C" fn do_call() -> ! {
+        naked_asm!(
+            "mv   t0, s0", // Function pointer in s0.
+            "mv   a0, s1", // Args
+            "mv   a1, s2",
+            "mv   a2, s3",
+            "mv   a3, s4",
+            "mv   a4, s5",
+            "mv   fp, zero", // Set frame pointer to 0.
+            "mv   ra, zero",
+            "jr   t0",
+        );
+    }
+}

+ 108 - 0
crates/eonix_hal/src/arch/riscv64/cpu.rs

@@ -0,0 +1,108 @@
+use super::{
+    interrupt::InterruptControl,
+    trap::{setup_trap, TRAP_SCRATCH},
+};
+use crate::arch::{
+    fdt::{FdtExt, FDT},
+    time::set_next_timer,
+};
+use core::{arch::asm, pin::Pin, ptr::NonNull};
+use eonix_preempt::PreemptGuard;
+use eonix_sync_base::LazyLock;
+use riscv::register::{
+    medeleg::{self, Medeleg},
+    mhartid, sscratch, sstatus,
+};
+use riscv_peripheral::plic::PLIC;
+use sbi::PhysicalAddress;
+
+#[eonix_percpu::define_percpu]
+static LOCAL_CPU: LazyLock<CPU> = LazyLock::new(CPU::new);
+
+#[derive(Debug, Clone)]
+pub enum UserTLS {
+    Base(u64),
+}
+
+/// RISC-V Hart
+pub struct CPU {
+    hart_id: usize,
+    interrupt: InterruptControl,
+}
+
+impl UserTLS {
+    pub fn new(base: u64) -> Self {
+        Self::Base(base)
+    }
+}
+
+impl CPU {
+    pub fn new() -> Self {
+        Self {
+            hart_id: 0,
+            interrupt: InterruptControl::new(),
+        }
+    }
+
+    /// Load CPU specific configurations for the current Hart.
+    ///
+    /// # Safety
+    /// This function performs low-level hardware initialization and should
+    /// only be called once per Hart during its boot sequence.
+    pub unsafe fn init(mut self: Pin<&mut Self>, hart_id: usize) {
+        let me = self.as_mut().get_unchecked_mut();
+        me.hart_id = hart_id;
+
+        setup_trap();
+
+        let interrupt = self.map_unchecked_mut(|me| &mut me.interrupt);
+        interrupt.init();
+
+        sstatus::set_sum();
+        sscratch::write(TRAP_SCRATCH.as_ptr() as usize);
+    }
+
+    /// Boot all other hart.
+    pub unsafe fn bootstrap_cpus(&self) {
+        let total_harts = FDT.hart_count();
+        for i in (0..total_harts).filter(|&i| i != self.hart_id) {
+            sbi::hsm::hart_start(i, todo!("AP entry"), 0)
+                .expect("Failed to start secondary hart via SBI");
+        }
+    }
+
+    pub unsafe fn load_interrupt_stack(self: Pin<&mut Self>, sp: u64) {
+        TRAP_SCRATCH
+            .as_mut()
+            .set_trap_context(NonNull::new(sp as *mut _).unwrap());
+    }
+
+    pub fn set_tls32(self: Pin<&mut Self>, _user_tls: &UserTLS) {
+        // nothing
+    }
+
+    pub fn end_of_interrupt(self: Pin<&mut Self>) {
+        // TODO: only timer interrupt should do this, here may need to change 
+        // if some other interrupt need send end signal
+        set_next_timer();
+    }
+
+    pub fn local() -> PreemptGuard<Pin<&'static mut Self>> {
+        unsafe {
+            // SAFETY: We pass the reference into a `PreemptGuard`, which ensures
+            //         that preemption is disabled.
+            PreemptGuard::new(Pin::new_unchecked(LOCAL_CPU.as_mut().get_mut()))
+        }
+    }
+
+    pub fn cpuid(&self) -> usize {
+        self.hart_id
+    }
+}
+
+#[inline(always)]
+pub fn halt() {
+    unsafe {
+        asm!("wfi", options(nomem, nostack, preserves_flags));
+    }
+}

+ 62 - 0
crates/eonix_hal/src/arch/riscv64/fdt.rs

@@ -0,0 +1,62 @@
+use super::mm::{ArchPhysAccess, PresentRam};
+use crate::arch::riscv64::config::mm::KIMAGE_OFFSET;
+use core::sync::atomic::{AtomicPtr, Ordering};
+use eonix_mm::address::{PAddr, PRange, PhysAccess};
+use eonix_sync_base::LazyLock;
+use fdt::Fdt;
+
+static DTB_VIRT_PTR: AtomicPtr<u8> = AtomicPtr::new(core::ptr::null_mut());
+pub static FDT: LazyLock<Fdt<'static>> = LazyLock::new(|| unsafe {
+    Fdt::from_ptr(DTB_VIRT_PTR.load(Ordering::Acquire))
+        .expect("Failed to parse DTB from static memory.")
+});
+
+pub trait FdtExt {
+    fn harts(&self) -> impl Iterator<Item = usize>;
+
+    fn hart_count(&self) -> usize {
+        self.harts().count()
+    }
+
+    fn present_ram(&self) -> impl Iterator<Item = PRange>;
+}
+
+impl FdtExt for Fdt<'_> {
+    fn harts(&self) -> impl Iterator<Item = usize> {
+        self.cpus().map(|cpu| cpu.ids().all()).flatten()
+    }
+
+    fn present_ram(&self) -> impl Iterator<Item = PRange> + PresentRam {
+        struct Present<I>(I);
+        impl<I> PresentRam for Present<I> where I: Iterator<Item = PRange> {}
+        impl<I> Iterator for Present<I>
+        where
+            I: Iterator<Item = PRange>,
+        {
+            type Item = PRange;
+
+            fn next(&mut self) -> Option<Self::Item> {
+                self.0.next()
+            }
+        }
+
+        let mut index = 0;
+        Present(core::iter::from_fn(move || {
+            self.memory()
+                .regions()
+                .filter_map(|region| {
+                    region.size.map(|len| {
+                        PRange::from(PAddr::from(region.starting_address as usize)).grow(len)
+                    })
+                })
+                .skip(index)
+                .next()
+                .inspect(|_| index += 1)
+        }))
+    }
+}
+
+pub unsafe fn init_dtb_and_fdt(dtb_paddr: PAddr) {
+    let dtb_virt_ptr = ArchPhysAccess::as_ptr(dtb_paddr);
+    DTB_VIRT_PTR.store(dtb_virt_ptr.as_ptr(), Ordering::Release);
+}

+ 58 - 0
crates/eonix_hal/src/arch/riscv64/fence.rs

@@ -0,0 +1,58 @@
+use core::{
+    arch::asm,
+    sync::atomic::{compiler_fence, Ordering},
+};
+
+#[doc(hidden)]
+/// Issues a full memory barrier.
+///
+/// Ensures all memory operations issued before the fence are globally
+/// visible before any memory operations issued after the fence.
+pub fn memory_barrier() {
+    unsafe {
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+
+        // rw for both predecessor and successor: read-write, read-write
+        asm!("fence rw, rw", options(nostack, nomem, preserves_flags));
+
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+    }
+}
+
+#[doc(hidden)]
+/// Issues a read memory barrier.
+///
+/// Ensures all memory loads issued before the fence are globally
+/// visible before any memory loads issued after the fence.
+pub fn read_memory_barrier() {
+    unsafe {
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+
+        // r for both predecessor and successor: read, read
+        asm!("fence r, r", options(nostack, nomem, preserves_flags));
+
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+    }
+}
+
+#[doc(hidden)]
+/// Issues a write memory barrier.
+///
+/// Ensures all memory stores issued before the fence are globally
+/// visible before any memory stores issued after the fence.
+pub fn write_memory_barrier() {
+    unsafe {
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+
+        // w for both predecessor and successor: write, write
+        asm!("fence w, w", options(nostack, nomem, preserves_flags));
+
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+    }
+}

+ 111 - 0
crates/eonix_hal/src/arch/riscv64/fpu.rs

@@ -0,0 +1,111 @@
+use core::arch::asm;
+use eonix_hal_traits::fpu::RawFpuState;
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+pub struct FpuState {
+    pub f: [u64; 32],
+    pub fcsr: u32,
+}
+
+impl RawFpuState for FpuState {
+    fn new() -> Self {
+        unsafe { core::mem::zeroed() }
+    }
+
+    /// Save reg -> mem
+    fn save(&mut self) {
+        unsafe {
+            let base_ptr: *mut u64 = self.f.as_mut_ptr();
+            let fcsr_ptr: *mut u32 = &mut self.fcsr;
+            let mut _fcsr_val: u32 = 0;
+            asm!(
+            "fsd f0,  (0 * 8)({base})",
+            "fsd f1,  (1 * 8)({base})",
+            "fsd f2,  (2 * 8)({base})",
+            "fsd f3,  (3 * 8)({base})",
+            "fsd f4,  (4 * 8)({base})",
+            "fsd f5,  (5 * 8)({base})",
+            "fsd f6,  (6 * 8)({base})",
+            "fsd f7,  (7 * 8)({base})",
+            "fsd f8,  (8 * 8)({base})",
+            "fsd f9,  (9 * 8)({base})",
+            "fsd f10, (10 * 8)({base})",
+            "fsd f11, (11 * 8)({base})",
+            "fsd f12, (12 * 8)({base})",
+            "fsd f13, (13 * 8)({base})",
+            "fsd f14, (14 * 8)({base})",
+            "fsd f15, (15 * 8)({base})",
+            "fsd f16, (16 * 8)({base})",
+            "fsd f17, (17 * 8)({base})",
+            "fsd f18, (18 * 8)({base})",
+            "fsd f19, (19 * 8)({base})",
+            "fsd f20, (20 * 8)({base})",
+            "fsd f21, (21 * 8)({base})",
+            "fsd f22, (22 * 8)({base})",
+            "fsd f23, (23 * 8)({base})",
+            "fsd f24, (24 * 8)({base})",
+            "fsd f25, (25 * 8)({base})",
+            "fsd f26, (26 * 8)({base})",
+            "fsd f27, (27 * 8)({base})",
+            "fsd f28, (28 * 8)({base})",
+            "fsd f29, (29 * 8)({base})",
+            "fsd f30, (30 * 8)({base})",
+            "fsd f31, (31 * 8)({base})",
+            "csrr {fcsr_val}, fcsr", // Read fcsr into fcsr_val (which is in a general-purpose register)
+            "sw {fcsr_val}, 0({fcsr_ptr})",
+            base = in(reg) base_ptr,
+            fcsr_val = out(reg) _fcsr_val,
+            fcsr_ptr = in(reg) fcsr_ptr,
+            options(nostack, nomem, preserves_flags));
+        }
+    }
+
+    fn restore(&mut self) {
+        let base_ptr: *const u64 = self.f.as_ptr();
+        let fcsr_ptr: *const u32 = &self.fcsr;
+        let mut _fcsr_val: u64;
+
+        unsafe {
+            asm!(
+            "fld f0,  (0 * 8)({base})",
+            "fld f1,  (1 * 8)({base})",
+            "fld f2,  (2 * 8)({base})",
+            "fld f3,  (3 * 8)({base})",
+            "fld f4,  (4 * 8)({base})",
+            "fld f5,  (5 * 8)({base})",
+            "fld f6,  (6 * 8)({base})",
+            "fld f7,  (7 * 8)({base})",
+            "fld f8,  (8 * 8)({base})",
+            "fld f9,  (9 * 8)({base})",
+            "fld f10, (10 * 8)({base})",
+            "fld f11, (11 * 8)({base})",
+            "fld f12, (12 * 8)({base})",
+            "fld f13, (13 * 8)({base})",
+            "fld f14, (14 * 8)({base})",
+            "fld f15, (15 * 8)({base})",
+            "fld f16, (16 * 8)({base})",
+            "fld f17, (17 * 8)({base})",
+            "fld f18, (18 * 8)({base})",
+            "fld f19, (19 * 8)({base})",
+            "fld f20, (20 * 8)({base})",
+            "fld f21, (21 * 8)({base})",
+            "fld f22, (22 * 8)({base})",
+            "fld f23, (23 * 8)({base})",
+            "fld f24, (24 * 8)({base})",
+            "fld f25, (25 * 8)({base})",
+            "fld f26, (26 * 8)({base})",
+            "fld f27, (27 * 8)({base})",
+            "fld f28, (28 * 8)({base})",
+            "fld f29, (29 * 8)({base})",
+            "fld f30, (30 * 8)({base})",
+            "fld f31, (31 * 8)({base})",
+            "lw {fcsr_val}, 0({fcsr_ptr})", // Load from memory (fcsr_ptr)
+            "csrw fcsr, {fcsr_val}",
+            base = in(reg) base_ptr,
+            fcsr_val = out(reg) _fcsr_val,
+            fcsr_ptr = in(reg) fcsr_ptr,
+            options(nostack, preserves_flags));
+        }
+    }
+}

+ 48 - 0
crates/eonix_hal/src/arch/riscv64/interrupt/mod.rs

@@ -0,0 +1,48 @@
+use core::pin::Pin;
+
+use crate::arch::time;
+
+use super::config::platform::virt::*;
+use riscv::register::sie;
+use riscv_peripheral::{
+    aclint::{Clint, CLINT},
+    plic::{Plic, PLIC},
+};
+use sbi::SbiError;
+
+#[derive(Clone, Copy)]
+struct ArchPlic;
+
+#[derive(Clone, Copy)]
+struct ArchClint;
+
+unsafe impl Plic for ArchPlic {
+    const BASE: usize = PLIC_BASE;
+}
+
+unsafe impl Clint for ArchClint {
+    const BASE: usize = CLINT_BASE;
+    const MTIME_FREQ: usize = CPU_FREQ_HZ as usize;
+}
+
+/// Architecture-specific interrupt control block.
+pub struct InterruptControl {
+    clint: CLINT<ArchClint>,
+}
+
+impl InterruptControl {
+    /// # Safety
+    /// should be called only once.
+    pub(crate) fn new() -> Self {
+        Self {
+            clint: CLINT::new(),
+        }
+    }
+
+    pub fn init(self: Pin<&mut Self>) {}
+}
+
+pub fn enable_timer_interrupt() {
+    unsafe { sie::set_stimer() };
+    time::set_next_timer();
+}

+ 91 - 0
crates/eonix_hal/src/arch/riscv64/link.x

@@ -0,0 +1,91 @@
+SECTIONS {
+    .bootstrap ORIGIN(RAM) :
+    {
+        KEEP(*(.bootstrap.entry .bootstrap.data));
+
+        . = ORIGIN(RAM) + 0x1000;
+        KEEP(*(.bootstrap.page_table.1));
+        KEEP(*(.bootstrap.page_table.2));
+
+        . = ALIGN(16);
+        KEEP(*(.bootstrap.stack));
+    } > RAM
+}
+INSERT BEFORE .text;
+
+SECTIONS {
+    .text.syscall_fns :
+    {
+
+        KEEP(*(.syscall_fns*));
+
+    } > REGION_TEXT AT> RAM
+}
+INSERT AFTER .text;
+
+SECTIONS {
+    .percpu : ALIGN(16)
+    {
+        __spercpu = .;
+
+        PERCPU_DATA_START = .;
+
+        . = ALIGN(16);
+
+        *(.percpu .percpu*);
+
+        . = ALIGN(16);
+        __epercpu = .;
+    } > REGION_RODATA AT> RAM
+
+    PERCPU_LENGTH = ABSOLUTE(__epercpu - __spercpu);
+
+    KIMAGE_PAGES = (__edata - _stext + 0x1000 - 1) / 0x1000;
+    KIMAGE_32K_COUNT = (KIMAGE_PAGES + 8 - 1) / 8;
+    __kernel_end = .;
+
+    BSS_LENGTH = ABSOLUTE(__ebss - __sbss);
+}
+INSERT AFTER .rodata;
+
+SECTIONS {
+    .rodata.syscalls :
+    {
+        . = ALIGN(16);
+        __raw_syscall_handlers_start = .;
+
+        RAW_SYSCALL_HANDLERS = .;
+        KEEP(*(.raw_syscalls*));
+
+        __raw_syscall_handlers_end = .;
+
+        RAW_SYSCALL_HANDLERS_SIZE =
+            ABSOLUTE(__raw_syscall_handlers_end - __raw_syscall_handlers_start);
+    } > REGION_RODATA AT> RAM
+}
+INSERT AFTER .rodata;
+
+SECTIONS {
+    .rodata.fixups :
+    {
+        . = ALIGN(16);
+        FIX_START = .;
+
+        KEEP(*(.fix));
+
+        FIX_END = .;
+    } > REGION_RODATA AT> RAM
+}
+INSERT AFTER .rodata;
+
+SECTIONS {
+    .vdso ALIGN(0x1000) : ALIGN(0x1000)
+    {
+        KEEP(*(.vdso .vdso.*));
+
+        . = ALIGN(0x1000);
+    } > VDSO AT> RAM
+
+    VDSO_PADDR = LOADADDR(.vdso);
+}
+INSERT AFTER .data;

+ 23 - 0
crates/eonix_hal/src/arch/riscv64/memory.x

@@ -0,0 +1,23 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+MEMORY {
+    RAM    : org = 0x0000000080200000, len = 4M
+    VDSO   : org = 0x00007f0000000000, len = 4K
+    KBSS   : org = 0xffffffff40000000, len = 2M
+    KIMAGE : org = 0xffffffff80200000, len = 2M
+}
+
+REGION_ALIAS("REGION_TEXT", KIMAGE);
+REGION_ALIAS("REGION_RODATA", KIMAGE);
+REGION_ALIAS("REGION_DATA", KIMAGE);
+REGION_ALIAS("REGION_BSS", KBSS);
+REGION_ALIAS("REGION_EHFRAME", KIMAGE);
+
+REGION_ALIAS("LINK_REGION_TEXT", RAM);
+REGION_ALIAS("LINK_REGION_RODATA", RAM);
+REGION_ALIAS("LINK_REGION_DATA", RAM);
+REGION_ALIAS("LINK_REGION_BSS", RAM);
+REGION_ALIAS("LINK_REGION_EHFRAME", RAM);
+
+_stext = ORIGIN(REGION_TEXT) + LOADADDR(.text) - ORIGIN(RAM);

+ 347 - 0
crates/eonix_hal/src/arch/riscv64/mm.rs

@@ -0,0 +1,347 @@
+use super::{
+    config::mm::{PHYS_MAP_VIRT, ROOT_PAGE_TABLE_PFN},
+    fdt::{FdtExt, FDT},
+};
+use crate::{arch::riscv64::config::mm::KIMAGE_OFFSET, traits::mm::Memory};
+use core::{marker::PhantomData, ptr::NonNull};
+use eonix_mm::{
+    address::{Addr as _, AddrOps, PAddr, PRange, PhysAccess, VAddr},
+    page_table::{
+        PageAttribute, PageTable, PageTableLevel, PagingMode, RawAttribute, RawPageTable,
+        TableAttribute, PTE,
+    },
+    paging::{NoAlloc, Page, PageBlock, PFN},
+};
+use eonix_sync_base::LazyLock;
+use fdt::Fdt;
+use riscv::{
+    asm::{sfence_vma, sfence_vma_all},
+    register::satp,
+};
+
+pub const PAGE_TABLE_BASE: PFN = PFN::from_val(ROOT_PAGE_TABLE_PFN);
+pub static GLOBAL_PAGE_TABLE: LazyLock<PageTable<ArchPagingMode, NoAlloc, ArchPhysAccess>> =
+    LazyLock::new(|| unsafe {
+        Page::with_raw(PAGE_TABLE_BASE, |root_table_page| {
+            PageTable::with_root_table(root_table_page.clone())
+        })
+    });
+
+pub const PA_V: u64 = 0b1 << 0;
+pub const PA_R: u64 = 0b1 << 1;
+pub const PA_W: u64 = 0b1 << 2;
+pub const PA_X: u64 = 0b1 << 3;
+pub const PA_U: u64 = 0b1 << 4;
+pub const PA_G: u64 = 0b1 << 5;
+pub const PA_A: u64 = 0b1 << 6;
+pub const PA_D: u64 = 0b1 << 7;
+
+// in RSW
+pub const PA_COW: u64 = 0b1 << 8;
+pub const PA_MMAP: u64 = 0b1 << 9;
+
+#[allow(dead_code)]
+pub const PA_SHIFT: u64 = 10;
+// Bit 0-9 (V, R, W, X, U, G, A, D, RSW)
+#[allow(dead_code)]
+pub const PA_FLAGS_MASK: u64 = 0x3FF; // 0b11_1111_1111
+
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct PTE64(pub u64);
+
+#[derive(Clone, Copy)]
+pub struct PageAttribute64(u64);
+
+pub struct RawPageTableSv48<'a>(NonNull<PTE64>, PhantomData<&'a ()>);
+
+pub struct PagingModeSv48;
+
+pub struct ArchPhysAccess;
+
+pub struct ArchMemory;
+
+impl PTE for PTE64 {
+    type Attr = PageAttribute64;
+
+    fn set(&mut self, pfn: PFN, attr: Self::Attr) {
+        self.0 = (usize::from(pfn) << PA_SHIFT) as u64 | attr.0;
+    }
+
+    fn get(&self) -> (PFN, Self::Attr) {
+        let pfn = PFN::from(self.0 as usize >> PA_SHIFT);
+        let attr = PageAttribute64(self.0 & PA_FLAGS_MASK);
+        (pfn, attr)
+    }
+}
+
+impl PagingMode for PagingModeSv48 {
+    type Entry = PTE64;
+    type RawTable<'a> = RawPageTableSv48<'a>;
+    const LEVELS: &'static [PageTableLevel] = &[
+        PageTableLevel::new(39, 9),
+        PageTableLevel::new(30, 9),
+        PageTableLevel::new(21, 9),
+        PageTableLevel::new(12, 9),
+    ];
+}
+
+pub type ArchPagingMode = PagingModeSv48;
+
+impl<'a> RawPageTable<'a> for RawPageTableSv48<'a> {
+    type Entry = PTE64;
+
+    fn index(&self, index: u16) -> &'a Self::Entry {
+        unsafe { self.0.add(index as usize).as_ref() }
+    }
+
+    fn index_mut(&mut self, index: u16) -> &'a mut Self::Entry {
+        unsafe { self.0.add(index as usize).as_mut() }
+    }
+
+    unsafe fn from_ptr(ptr: NonNull<PageBlock>) -> Self {
+        Self(ptr.cast(), PhantomData)
+    }
+}
+
+impl RawAttribute for PageAttribute64 {
+    fn null() -> Self {
+        Self(0)
+    }
+
+    fn as_table_attr(self) -> Option<TableAttribute> {
+        let mut table_attr = TableAttribute::empty();
+
+        if self.0 & PA_V != 0 {
+            table_attr |= TableAttribute::PRESENT;
+        }
+
+        if table_attr.contains(TableAttribute::PRESENT) && self.0 & (PA_R | PA_W | PA_X) != 0 {
+            return None;
+        }
+
+        if self.0 & PA_G != 0 {
+            table_attr |= TableAttribute::GLOBAL;
+        }
+        if self.0 & PA_U != 0 {
+            table_attr |= TableAttribute::USER;
+        }
+        if self.0 & PA_A != 0 {
+            table_attr |= TableAttribute::ACCESSED;
+        }
+
+        Some(table_attr)
+    }
+
+    fn as_page_attr(self) -> Option<PageAttribute> {
+        let mut page_attr = PageAttribute::empty();
+
+        if self.0 & PA_V != 0 {
+            page_attr |= PageAttribute::PRESENT;
+        }
+
+        if page_attr.contains(PageAttribute::PRESENT) && (self.0 & (PA_R | PA_W | PA_X) == 0) {
+            return None;
+        }
+
+        if self.0 & PA_R != 0 {
+            page_attr |= PageAttribute::READ;
+        }
+
+        if self.0 & PA_W != 0 {
+            page_attr |= PageAttribute::WRITE;
+        }
+
+        if self.0 & PA_X != 0 {
+            page_attr |= PageAttribute::EXECUTE;
+        }
+
+        if self.0 & PA_U != 0 {
+            page_attr |= PageAttribute::USER;
+        }
+
+        if self.0 & PA_A != 0 {
+            page_attr |= PageAttribute::ACCESSED;
+        }
+
+        if self.0 & PA_D != 0 {
+            page_attr |= PageAttribute::DIRTY;
+        }
+
+        if self.0 & PA_G != 0 {
+            page_attr |= PageAttribute::GLOBAL;
+        }
+
+        if self.0 & PA_COW != 0 {
+            page_attr |= PageAttribute::COPY_ON_WRITE;
+        }
+
+        if self.0 & PA_MMAP != 0 {
+            page_attr |= PageAttribute::MAPPED;
+        }
+
+        /*if self.0 & PA_ANON != 0 {
+            page_attr |= PageAttribute::ANONYMOUS;
+        }*/
+
+        Some(page_attr)
+    }
+}
+
+impl From<PageAttribute> for PageAttribute64 {
+    fn from(page_attr: PageAttribute) -> Self {
+        let mut raw_attr = 0;
+
+        for attr in page_attr.iter() {
+            match attr {
+                PageAttribute::PRESENT => raw_attr |= PA_V,
+                PageAttribute::READ => raw_attr |= PA_R,
+                PageAttribute::WRITE => raw_attr |= PA_W,
+                PageAttribute::EXECUTE => raw_attr |= PA_X,
+                PageAttribute::USER => raw_attr |= PA_U,
+                PageAttribute::ACCESSED => raw_attr |= PA_A,
+                PageAttribute::DIRTY => raw_attr |= PA_D,
+                PageAttribute::GLOBAL => raw_attr |= PA_G,
+                PageAttribute::COPY_ON_WRITE => raw_attr |= PA_COW,
+                PageAttribute::MAPPED => raw_attr |= PA_MMAP,
+                PageAttribute::ANONYMOUS => {}
+                _ => unreachable!("Invalid page attribute"),
+            }
+        }
+
+        Self(raw_attr)
+    }
+}
+
+impl From<TableAttribute> for PageAttribute64 {
+    fn from(table_attr: TableAttribute) -> Self {
+        let mut raw_attr = 0;
+
+        for attr in table_attr.iter() {
+            match attr {
+                TableAttribute::PRESENT => raw_attr |= PA_V,
+                TableAttribute::GLOBAL => raw_attr |= PA_G,
+                TableAttribute::USER | TableAttribute::ACCESSED => {}
+                _ => unreachable!("Invalid table attribute"),
+            }
+        }
+
+        Self(raw_attr)
+    }
+}
+
+impl ArchPhysAccess {
+    const PHYS_OFFSET: usize = PHYS_MAP_VIRT;
+}
+
+impl PhysAccess for ArchPhysAccess {
+    unsafe fn as_ptr<T>(paddr: PAddr) -> NonNull<T> {
+        let alignment: usize = align_of::<T>();
+        assert!(paddr.addr() % alignment == 0, "Alignment error");
+
+        unsafe {
+            // SAFETY: We can assume that we'll never have `self.addr()` equals
+            //         to `-PHYS_OFFSET`. Otherwise, the kernel might be broken.
+            NonNull::new_unchecked((Self::PHYS_OFFSET + paddr.addr()) as *mut T)
+        }
+    }
+
+    unsafe fn from_ptr<T>(ptr: NonNull<T>) -> PAddr {
+        let addr = ptr.addr().get();
+
+        assert!(addr % align_of::<T>() == 0, "Alignment error");
+        assert!(
+            addr >= Self::PHYS_OFFSET,
+            "Address is not a valid physical address"
+        );
+
+        PAddr::from_val(addr - Self::PHYS_OFFSET)
+    }
+}
+
+impl Memory for ArchMemory {
+    fn present_ram() -> impl Iterator<Item = PRange> {
+        FDT.present_ram()
+    }
+
+    fn free_ram() -> impl Iterator<Item = PRange> {
+        unsafe extern "C" {
+            fn __kernel_start();
+            fn __kernel_end();
+        }
+
+        let kernel_end = PAddr::from(__kernel_end as usize - KIMAGE_OFFSET);
+        let paddr_after_kimage_aligned = kernel_end.ceil_to(0x200000);
+
+        core::iter::once(PRange::new(kernel_end, paddr_after_kimage_aligned)).chain(
+            Self::present_ram()
+                .filter(move |range| range.end() > paddr_after_kimage_aligned)
+                .map(move |range| {
+                    if range.start() < paddr_after_kimage_aligned {
+                        let (_, right) = range.split_at(paddr_after_kimage_aligned);
+                        right
+                    } else {
+                        range
+                    }
+                }),
+        )
+    }
+}
+
+pub type DefaultPagingMode = PagingModeSv48;
+
+pub trait PresentRam: Iterator<Item = PRange> {}
+
+pub trait FreeRam: PresentRam {
+    fn free_ram(self) -> impl Iterator<Item = PRange>;
+}
+
+impl<T> FreeRam for T
+where
+    T: PresentRam,
+{
+    fn free_ram(self) -> impl Iterator<Item = PRange> {
+        unsafe extern "C" {
+            fn __kernel_start();
+            fn __kernel_end();
+        }
+
+        let kernel_end = PAddr::from(__kernel_end as usize - KIMAGE_OFFSET);
+        let paddr_after_kimage_aligned = kernel_end.ceil_to(0x200000);
+
+        core::iter::once(PRange::new(kernel_end, paddr_after_kimage_aligned)).chain(
+            self.filter(move |range| range.end() > paddr_after_kimage_aligned)
+                .map(move |range| {
+                    if range.start() < paddr_after_kimage_aligned {
+                        let (_, right) = range.split_at(paddr_after_kimage_aligned);
+                        right
+                    } else {
+                        range
+                    }
+                }),
+        )
+    }
+}
+
+#[inline(always)]
+pub fn flush_tlb(vaddr: usize) {
+    sfence_vma(0, vaddr);
+}
+
+#[inline(always)]
+pub fn flush_tlb_all() {
+    sfence_vma_all();
+}
+
+#[inline(always)]
+pub fn get_root_page_table_pfn() -> PFN {
+    let satp_val = satp::read();
+    let ppn = satp_val.ppn();
+    PFN::from(ppn)
+}
+
+#[inline(always)]
+pub fn set_root_page_table_pfn(pfn: PFN) {
+    unsafe { satp::set(satp::Mode::Sv48, 0, usize::from(pfn)) };
+    sfence_vma_all();
+}

+ 12 - 0
crates/eonix_hal/src/arch/riscv64/mod.rs

@@ -0,0 +1,12 @@
+pub mod bootstrap;
+mod config;
+pub mod console;
+pub mod context;
+pub mod cpu;
+pub mod fdt;
+pub mod fence;
+pub mod fpu;
+pub mod interrupt;
+pub mod mm;
+pub mod trap;
+pub mod time;

+ 18 - 0
crates/eonix_hal/src/arch/riscv64/time.rs

@@ -0,0 +1,18 @@
+use riscv::register::time;
+use super::config::{
+    platform::virt::CPU_FREQ_HZ,
+    time::INTERRUPTS_PER_SECOND
+};
+
+pub fn get_time() -> usize {
+    time::read()
+}
+
+pub fn set_timer(times: usize) {
+    let time = (get_time() + times * CPU_FREQ_HZ as usize / INTERRUPTS_PER_SECOND) as u64;
+    sbi::legacy::set_timer(time);
+}
+
+pub fn set_next_timer() {
+    set_timer(1);
+}

+ 359 - 0
crates/eonix_hal/src/arch/riscv64/trap/mod.rs

@@ -0,0 +1,359 @@
+mod trap_context;
+
+use super::config::platform::virt::*;
+use super::context::TaskContext;
+use core::arch::{global_asm, naked_asm};
+use core::mem::{offset_of, size_of};
+use core::num::NonZero;
+use core::ptr::NonNull;
+use eonix_hal_traits::{
+    context::RawTaskContext,
+    trap::{IrqState as IrqStateTrait, TrapReturn},
+};
+use riscv::register::sstatus::{self, Sstatus};
+use riscv::register::stvec::TrapMode;
+use riscv::register::{scause, sepc, stval};
+use riscv::{
+    asm::sfence_vma_all,
+    register::{
+        sie,
+        stvec::{self, Stvec},
+    },
+};
+use sbi::SbiError;
+
+pub use trap_context::*;
+
+#[repr(C)]
+pub struct TrapScratch {
+    t1: u64,
+    t2: u64,
+    kernel_tp: Option<NonZero<u64>>,
+    trap_context: Option<NonNull<TrapContext>>,
+    handler: unsafe extern "C" fn(),
+    capturer_context: TaskContext,
+}
+
+#[eonix_percpu::define_percpu]
+pub(crate) static TRAP_SCRATCH: TrapScratch = TrapScratch {
+    t1: 0,
+    t2: 0,
+    kernel_tp: None,
+    trap_context: None,
+    handler: default_trap_handler,
+    capturer_context: TaskContext::new(),
+};
+
+static mut DIRTY_TASK_CONTEXT: TaskContext = TaskContext::new();
+
+#[unsafe(naked)]
+unsafe extern "C" fn _raw_trap_entry() -> ! {
+    naked_asm!(
+        "csrrw t0, sscratch, t0", // Swap t0 and sscratch
+        "sd    t1, 0(t0)",
+        "sd    t2, 8(t0)",
+        "csrr  t1, sstatus",
+        "andi  t1, t1, 0x100",
+        "beqz  t1, 2f",
+        // else SPP = 1, supervisor mode
+        "addi  t1, sp, -{trap_context_size}",
+        "mv    t2, tp",
+        "sd    ra, {ra}(t1)",
+        "sd    sp, {sp}(t1)",
+        "mv    sp, t1",
+        "j     4f",
+        // SPP = 0, user mode
+        "2:",
+        "ld    t1, 24(t0)", // Load captured TrapContext address
+        "mv    t2, tp",
+        "ld    tp, 16(t0)", // Restore kernel tp
+        // t0: &mut TrapScratch, t1: &mut TrapContext, t2: tp before trap
+        "3:",
+        "sd    ra, {ra}(t1)",
+        "sd    sp, {sp}(t1)",
+        "4:",
+        "sd    gp, {gp}(t1)",
+        "sd    t2, {tp}(t1)",
+        "ld    ra, 0(t0)",
+        "ld    t2, 8(t0)",
+        "sd    ra, {t1}(t1)",     // Save t1
+        "sd    t2, {t2}(t1)",     // Save t2
+        "ld    ra, 32(t0)",       // Load handler address
+        "csrrw t2, sscratch, t0", // Swap t0 and sscratch
+        "sd    t2, {t0}(t1)",
+        "sd    a0, {a0}(t1)",
+        "sd    a1, {a1}(t1)",
+        "sd    a2, {a2}(t1)",
+        "sd    a3, {a3}(t1)",
+        "sd    a4, {a4}(t1)",
+        "sd    a5, {a5}(t1)",
+        "sd    a6, {a6}(t1)",
+        "sd    a7, {a7}(t1)",
+        "sd    t3, {t3}(t1)",
+        "sd    t4, {t4}(t1)",
+        "sd    t5, {t5}(t1)",
+        "sd    t6, {t6}(t1)",
+        "sd    s0, {s0}(t1)",
+        "sd    s1, {s1}(t1)",
+        "sd    s2, {s2}(t1)",
+        "sd    s3, {s3}(t1)",
+        "sd    s4, {s4}(t1)",
+        "sd    s5, {s5}(t1)",
+        "sd    s6, {s6}(t1)",
+        "sd    s7, {s7}(t1)",
+        "sd    s8, {s8}(t1)",
+        "sd    s9, {s9}(t1)",
+        "sd    s10, {s10}(t1)",
+        "sd    s11, {s11}(t1)",
+        "csrr  t2, sstatus",
+        "csrr  t3, sepc",
+        "csrr  t4, scause",
+        "sd    t2, {sstatus}(t1)",
+        "sd    t3, {sepc}(t1)",
+        "sd    t4, {scause}(t1)",
+        "ret",
+        trap_context_size = const size_of::<TrapContext>(),
+        ra = const Registers::OFFSET_RA,
+        sp = const Registers::OFFSET_SP,
+        gp = const Registers::OFFSET_GP,
+        tp = const Registers::OFFSET_TP,
+        t1 = const Registers::OFFSET_T1,
+        t2 = const Registers::OFFSET_T2,
+        t0 = const Registers::OFFSET_T0,
+        a0 = const Registers::OFFSET_A0,
+        a1 = const Registers::OFFSET_A1,
+        a2 = const Registers::OFFSET_A2,
+        a3 = const Registers::OFFSET_A3,
+        a4 = const Registers::OFFSET_A4,
+        a5 = const Registers::OFFSET_A5,
+        a6 = const Registers::OFFSET_A6,
+        a7 = const Registers::OFFSET_A7,
+        t3 = const Registers::OFFSET_T3,
+        t4 = const Registers::OFFSET_T4,
+        t5 = const Registers::OFFSET_T5,
+        t6 = const Registers::OFFSET_T6,
+        s0 = const Registers::OFFSET_S0,
+        s1 = const Registers::OFFSET_S1,
+        s2 = const Registers::OFFSET_S2,
+        s3 = const Registers::OFFSET_S3,
+        s4 = const Registers::OFFSET_S4,
+        s5 = const Registers::OFFSET_S5,
+        s6 = const Registers::OFFSET_S6,
+        s7 = const Registers::OFFSET_S7,
+        s8 = const Registers::OFFSET_S8,
+        s9 = const Registers::OFFSET_S9,
+        s10 = const Registers::OFFSET_S10,
+        s11 = const Registers::OFFSET_S11,
+        sstatus = const TrapContext::OFFSET_SSTATUS,
+        sepc = const TrapContext::OFFSET_SEPC,
+        scause = const TrapContext::OFFSET_SCAUSE,
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn _raw_trap_return(ctx: &mut TrapContext) -> ! {
+    naked_asm!(
+        "ld ra, {ra}(a0)",
+        "ld sp, {sp}(a0)",
+        "ld gp, {gp}(a0)",
+        "ld tp, {tp}(a0)",
+        "ld t1, {t1}(a0)",
+        "ld t2, {t2}(a0)",
+        "ld t0, {t0}(a0)",
+        "ld a1, {a1}(a0)",
+        "ld a2, {a2}(a0)",
+        "ld a3, {a3}(a0)",
+        "ld a4, {a4}(a0)",
+        "ld a5, {a5}(a0)",
+        "ld a6, {a6}(a0)",
+        "ld a7, {a7}(a0)",
+        "ld t3, {t3}(a0)",
+        "ld t4, {sepc}(a0)",    // Load sepc from TrapContext
+        "ld t5, {sstatus}(a0)", // Load sstatus from TrapContext
+        "ld s0, {s0}(a0)",
+        "ld s1, {s1}(a0)",
+        "ld s2, {s2}(a0)",
+        "ld s3, {s3}(a0)",
+        "ld s4, {s4}(a0)",
+        "ld s5, {s5}(a0)",
+        "ld s6, {s6}(a0)",
+        "ld s7, {s7}(a0)",
+        "ld s8, {s8}(a0)",
+        "ld s9, {s9}(a0)",
+        "ld s10, {s10}(a0)",
+        "ld s11, {s11}(a0)",
+        "csrw sepc, t4",        // Restore sepc
+        "csrw sstatus, t5",     // Restore sstatus
+        "ld t4, {t4}(a0)",
+        "ld t5, {t5}(a0)",
+        "ld t6, {t6}(a0)",
+        "ld a0, {a0}(a0)",
+        "sret",
+        ra = const Registers::OFFSET_RA,
+        sp = const Registers::OFFSET_SP,
+        gp = const Registers::OFFSET_GP,
+        tp = const Registers::OFFSET_TP,
+        t1 = const Registers::OFFSET_T1,
+        t2 = const Registers::OFFSET_T2,
+        t0 = const Registers::OFFSET_T0,
+        a0 = const Registers::OFFSET_A0,
+        a1 = const Registers::OFFSET_A1,
+        a2 = const Registers::OFFSET_A2,
+        a3 = const Registers::OFFSET_A3,
+        a4 = const Registers::OFFSET_A4,
+        a5 = const Registers::OFFSET_A5,
+        a6 = const Registers::OFFSET_A6,
+        a7 = const Registers::OFFSET_A7,
+        t3 = const Registers::OFFSET_T3,
+        t4 = const Registers::OFFSET_T4,
+        t5 = const Registers::OFFSET_T5,
+        t6 = const Registers::OFFSET_T6,
+        s0 = const Registers::OFFSET_S0,
+        s1 = const Registers::OFFSET_S1,
+        s2 = const Registers::OFFSET_S2,
+        s3 = const Registers::OFFSET_S3,
+        s4 = const Registers::OFFSET_S4,
+        s5 = const Registers::OFFSET_S5,
+        s6 = const Registers::OFFSET_S6,
+        s7 = const Registers::OFFSET_S7,
+        s8 = const Registers::OFFSET_S8,
+        s9 = const Registers::OFFSET_S9,
+        s10 = const Registers::OFFSET_S10,
+        s11 = const Registers::OFFSET_S11,
+        sstatus = const TrapContext::OFFSET_SSTATUS,
+        sepc = const TrapContext::OFFSET_SEPC,
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn default_trap_handler() {
+    unsafe extern "C" {
+        fn _default_trap_handler(trap_context: &mut TrapContext);
+    }
+
+    naked_asm!(
+        "andi sp, sp, -16", // Align stack pointer to 16 bytes
+        "addi sp, sp, -16",
+        "mv   a0, t1",      // TrapContext pointer in t1
+        "sd   a0, 0(sp)",   // Save TrapContext pointer
+        "",
+        "call {default_handler}",
+        "",
+        "ld   a0, 0(sp)",   // Restore TrapContext pointer
+        "j {trap_return}",
+        default_handler = sym _default_trap_handler,
+        trap_return = sym _raw_trap_return,
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn captured_trap_handler() {
+    naked_asm!(
+        "la   a0, {dirty_task_context}",
+        "addi a1, t0, {capturer_context_offset}",
+        "j {switch}",
+        dirty_task_context = sym DIRTY_TASK_CONTEXT,
+        capturer_context_offset = const offset_of!(TrapScratch, capturer_context),
+        switch = sym TaskContext::switch,
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn captured_trap_return(trap_context: usize) -> ! {
+    naked_asm!(
+        "mv a0, sp",
+        "j {raw_trap_return}",
+        raw_trap_return = sym _raw_trap_return,
+    );
+}
+
+impl TrapScratch {
+    pub fn set_trap_context(&mut self, ctx: NonNull<TrapContext>) {
+        self.trap_context = Some(ctx);
+    }
+
+    pub fn clear_trap_context(&mut self) {
+        self.trap_context = None;
+    }
+
+    pub fn set_kernel_tp(&mut self, tp: NonNull<u8>) {
+        self.kernel_tp = Some(NonZero::new(tp.addr().get() as u64).unwrap());
+    }
+}
+
+impl TrapReturn for TrapContext {
+    type TaskContext = TaskContext;
+
+    unsafe fn trap_return(&mut self) {
+        let irq_states = disable_irqs_save();
+        let old_handler =
+            core::mem::replace(&mut TRAP_SCRATCH.as_mut().handler, captured_trap_handler);
+
+        let mut to_ctx = TaskContext::new();
+        to_ctx.set_program_counter(captured_trap_return as usize);
+        to_ctx.set_stack_pointer(&raw mut *self as usize);
+        to_ctx.set_interrupt_enabled(false);
+
+        unsafe {
+            TaskContext::switch(&mut TRAP_SCRATCH.as_mut().capturer_context, &mut to_ctx);
+        }
+
+        TRAP_SCRATCH.as_mut().handler = old_handler;
+        irq_states.restore();
+    }
+}
+
+fn setup_trap_handler(trap_entry_addr: usize) {
+    let mut stvec_val = Stvec::from_bits(0);
+    stvec_val.set_address(trap_entry_addr);
+    stvec_val.set_trap_mode(TrapMode::Direct);
+
+    unsafe {
+        stvec::write(stvec_val);
+    }
+}
+
+pub fn setup_trap() {
+    setup_trap_handler(_raw_trap_entry as usize);
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct IrqState(Sstatus);
+
+impl IrqState {
+    #[inline]
+    pub fn save() -> Self {
+        IrqState(sstatus::read())
+    }
+}
+
+impl IrqStateTrait for IrqState {
+    fn restore(self) {
+        let Self(state) = self;
+        unsafe {
+            sstatus::write(state);
+        }
+    }
+}
+
+#[inline]
+pub fn disable_irqs() {
+    unsafe {
+        sstatus::clear_sie();
+    }
+}
+
+#[inline]
+pub fn enable_irqs() {
+    unsafe {
+        sstatus::set_sie();
+    }
+}
+
+#[inline]
+pub fn disable_irqs_save() -> IrqState {
+    let state = IrqState::save();
+    disable_irqs();
+
+    state
+}

+ 277 - 0
crates/eonix_hal/src/arch/riscv64/trap/trap_context.rs

@@ -0,0 +1,277 @@
+use core::arch::asm;
+use eonix_hal_traits::{
+    fault::{Fault, PageFaultErrorCode},
+    trap::{RawTrapContext, TrapType},
+};
+use eonix_mm::address::VAddr;
+use riscv::{
+    interrupt::{Exception, Interrupt, Trap},
+    register::{
+        scause::{self, Scause},
+        sstatus::{self, Sstatus, FS, SPP},
+        stval,
+    },
+    ExceptionNumber, InterruptNumber,
+};
+
+#[repr(C)]
+#[derive(Default, Clone, Copy)]
+pub struct Registers {
+    ra: u64,
+    sp: u64,
+    gp: u64,
+    tp: u64,
+    t1: u64,
+    t2: u64,
+    t0: u64,
+    a0: u64,
+    a1: u64,
+    a2: u64,
+    a3: u64,
+    a4: u64,
+    a5: u64,
+    a6: u64,
+    a7: u64,
+    t3: u64,
+    t4: u64,
+    t5: u64,
+    t6: u64,
+    s0: u64,
+    s1: u64,
+    s2: u64,
+    s3: u64,
+    s4: u64,
+    s5: u64,
+    s6: u64,
+    s7: u64,
+    s8: u64,
+    s9: u64,
+    s10: u64,
+    s11: u64,
+}
+
+/// Saved CPU context when a trap (interrupt or exception) occurs on RISC-V 64.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct TrapContext {
+    regs: Registers,
+
+    sstatus: Sstatus,
+    sepc: usize,
+    scause: Scause,
+}
+
+impl Registers {
+    pub const OFFSET_RA: usize = 0 * 8;
+    pub const OFFSET_SP: usize = 1 * 8;
+    pub const OFFSET_GP: usize = 2 * 8;
+    pub const OFFSET_TP: usize = 3 * 8;
+    pub const OFFSET_T1: usize = 4 * 8;
+    pub const OFFSET_T2: usize = 5 * 8;
+    pub const OFFSET_T0: usize = 6 * 8;
+    pub const OFFSET_A0: usize = 7 * 8;
+    pub const OFFSET_A1: usize = 8 * 8;
+    pub const OFFSET_A2: usize = 9 * 8;
+    pub const OFFSET_A3: usize = 10 * 8;
+    pub const OFFSET_A4: usize = 11 * 8;
+    pub const OFFSET_A5: usize = 12 * 8;
+    pub const OFFSET_A6: usize = 13 * 8;
+    pub const OFFSET_A7: usize = 14 * 8;
+    pub const OFFSET_T3: usize = 15 * 8;
+    pub const OFFSET_T4: usize = 16 * 8;
+    pub const OFFSET_T5: usize = 17 * 8;
+    pub const OFFSET_T6: usize = 18 * 8;
+    pub const OFFSET_S0: usize = 19 * 8;
+    pub const OFFSET_S1: usize = 20 * 8;
+    pub const OFFSET_S2: usize = 21 * 8;
+    pub const OFFSET_S3: usize = 22 * 8;
+    pub const OFFSET_S4: usize = 23 * 8;
+    pub const OFFSET_S5: usize = 24 * 8;
+    pub const OFFSET_S6: usize = 25 * 8;
+    pub const OFFSET_S7: usize = 26 * 8;
+    pub const OFFSET_S8: usize = 27 * 8;
+    pub const OFFSET_S9: usize = 28 * 8;
+    pub const OFFSET_S10: usize = 29 * 8;
+    pub const OFFSET_S11: usize = 30 * 8;
+}
+
+impl TrapContext {
+    pub const OFFSET_SSTATUS: usize = 31 * 8;
+    pub const OFFSET_SEPC: usize = 32 * 8;
+    pub const OFFSET_SCAUSE: usize = 33 * 8;
+
+    fn syscall_no(&self) -> usize {
+        self.regs.a7 as usize
+    }
+
+    fn syscall_args(&self) -> [usize; 6] {
+        [
+            self.regs.a0 as usize,
+            self.regs.a1 as usize,
+            self.regs.a2 as usize,
+            self.regs.a3 as usize,
+            self.regs.a4 as usize,
+            self.regs.a5 as usize,
+        ]
+    }
+}
+
+impl RawTrapContext for TrapContext {
+    fn new() -> Self {
+        let mut sstatus = Sstatus::from_bits(0);
+        sstatus.set_fs(FS::Initial);
+        sstatus.set_sum(true);
+
+        Self {
+            regs: Registers::default(),
+            sstatus,
+            sepc: 0,
+            scause: Scause::from_bits(0),
+        }
+    }
+
+    fn trap_type(&self) -> TrapType {
+        let cause = self.scause.cause();
+        match cause {
+            Trap::Interrupt(i) => {
+                match Interrupt::from_number(i).unwrap() {
+                    Interrupt::SupervisorTimer => TrapType::Timer,
+                    // TODO: need to read plic
+                    Interrupt::SupervisorExternal => TrapType::Irq(0),
+                    // soft interrupt
+                    _ => TrapType::Fault(Fault::Unknown(i)),
+                }
+            }
+            Trap::Exception(e) => {
+                match Exception::from_number(e).unwrap() {
+                    Exception::InstructionMisaligned
+                    | Exception::LoadMisaligned
+                    | Exception::InstructionFault
+                    | Exception::LoadFault
+                    | Exception::StoreFault
+                    | Exception::StoreMisaligned => TrapType::Fault(Fault::BadAccess),
+                    Exception::IllegalInstruction => TrapType::Fault(Fault::InvalidOp),
+                    Exception::UserEnvCall => TrapType::Syscall {
+                        no: self.syscall_no(),
+                        args: self.syscall_args(),
+                    },
+                    exception @ (Exception::InstructionPageFault
+                    | Exception::LoadPageFault
+                    | Exception::StorePageFault) => {
+                        #[inline(always)]
+                        fn get_page_fault_address() -> VAddr {
+                            VAddr::from(stval::read())
+                        }
+                        TrapType::Fault(Fault::PageFault {
+                            error_code: self.get_page_fault_error_code(exception),
+                            address: get_page_fault_address(),
+                        })
+                    }
+                    // breakpoint and supervisor env call
+                    _ => TrapType::Fault(Fault::Unknown(e)),
+                }
+            }
+        }
+    }
+
+    fn get_program_counter(&self) -> usize {
+        self.sepc
+    }
+
+    fn get_stack_pointer(&self) -> usize {
+        self.regs.sp as usize
+    }
+
+    fn set_program_counter(&mut self, pc: usize) {
+        self.sepc = pc;
+    }
+
+    fn set_stack_pointer(&mut self, sp: usize) {
+        self.regs.sp = sp as u64;
+    }
+
+    fn is_interrupt_enabled(&self) -> bool {
+        self.sstatus.spie()
+    }
+
+    fn set_interrupt_enabled(&mut self, enabled: bool) {
+        self.sstatus.set_spie(enabled);
+    }
+
+    fn is_user_mode(&self) -> bool {
+        self.sstatus.spp() == SPP::User
+    }
+
+    fn set_user_mode(&mut self, user: bool) {
+        match user {
+            true => self.sstatus.set_spp(SPP::User),
+            false => self.sstatus.set_spp(SPP::Supervisor),
+        }
+    }
+
+    fn set_user_return_value(&mut self, retval: usize) {
+        self.regs.a0 = retval as u64;
+    }
+
+    fn set_user_call_frame<E>(
+        &mut self,
+        pc: usize,
+        sp: Option<usize>,
+        ra: Option<usize>,
+        args: &[usize],
+        _write_memory: impl Fn(VAddr, &[u8]) -> Result<(), E>,
+    ) -> Result<(), E> {
+        self.set_program_counter(pc);
+
+        if let Some(sp) = sp {
+            self.set_stack_pointer(sp);
+        }
+
+        if let Some(ra) = ra {
+            self.regs.ra = ra as u64;
+        }
+
+        let arg_regs = [
+            &mut self.regs.a0,
+            &mut self.regs.a1,
+            &mut self.regs.a2,
+            &mut self.regs.a3,
+            &mut self.regs.a4,
+            &mut self.regs.a5,
+        ];
+
+        for (&arg, reg) in args.iter().zip(arg_regs.into_iter()) {
+            *reg = arg as u64;
+        }
+
+        Ok(())
+    }
+}
+
+impl TrapContext {
+    /// TODO: get PageFaultErrorCode also need check pagetable
+    fn get_page_fault_error_code(&self, exception: Exception) -> PageFaultErrorCode {
+        let mut error_code = PageFaultErrorCode::empty();
+
+        match exception {
+            Exception::InstructionPageFault => {
+                error_code |= PageFaultErrorCode::InstructionFetch;
+            }
+            Exception::LoadPageFault => {
+                error_code |= PageFaultErrorCode::Read;
+            }
+            Exception::StorePageFault => {
+                error_code |= PageFaultErrorCode::Write;
+            }
+            _ => {
+                unreachable!();
+            }
+        }
+
+        if self.sstatus.spp() == SPP::User {
+            error_code |= PageFaultErrorCode::UserAccess;
+        }
+
+        error_code
+    }
+}

+ 3 - 16
crates/eonix_hal/src/arch/x86_64/bootstrap/init.rs

@@ -1,14 +1,14 @@
 use crate::{
     arch::{
         bootstrap::{EARLY_GDT_DESCRIPTOR, KERNEL_PML4},
-        cpu::CPU,
+        cpu::{wrmsr, CPU},
+        io::Port8,
         mm::{ArchPhysAccess, GLOBAL_PAGE_TABLE, V_KERNEL_BSS_START},
     },
     bootstrap::BootStrapData,
     mm::{ArchMemory, ArchPagingMode, BasicPageAlloc, BasicPageAllocRef, ScopedAllocator},
 };
 use acpi::{platform::ProcessorState, AcpiHandler, AcpiTables, PhysicalMapping, PlatformInfo};
-use arch::wrmsr;
 use core::{
     alloc::Allocator,
     arch::{asm, global_asm},
@@ -160,19 +160,6 @@ fn setup_cpu(alloc: impl PageAlloc) {
 
 fn setup_pic() {
     // TODO: Remove this when we have completely switched to APIC.
-    pub struct Port8 {
-        no: u16,
-    }
-
-    impl Port8 {
-        pub const fn new(no: u16) -> Self {
-            Self { no }
-        }
-
-        pub fn write(&self, data: u8) {
-            arch::outb(self.no, data)
-        }
-    }
 
     const PIC1_COMMAND: Port8 = Port8::new(0x20);
     const PIC1_DATA: Port8 = Port8::new(0x21);
@@ -241,7 +228,7 @@ fn bootstrap_smp(alloc: impl Allocator, page_alloc: &RefCell<BasicPageAlloc>) {
         let stack_range = {
             let page_alloc = BasicPageAllocRef::new(&page_alloc);
 
-            let ap_stack = Page::alloc_order_in(3, page_alloc);
+            let ap_stack = Page::alloc_order_in(4, page_alloc);
             let stack_range = ap_stack.range();
             ap_stack.into_raw();
 

+ 30 - 36
crates/eonix_hal/src/arch/x86_64/context.rs

@@ -35,39 +35,6 @@ impl TaskContext {
         }
     }
 
-    #[unsafe(naked)]
-    unsafe extern "C" fn _switch(from: &mut Self, to: &mut Self) {
-        naked_asm!(
-            "pop %rax",
-            "pushf",
-            "pop %rcx",
-            "mov %r12, (%rdi)",
-            "mov %r13, 8(%rdi)",
-            "mov %r14, 16(%rdi)",
-            "mov %r15, 24(%rdi)",
-            "mov %rbx, 32(%rdi)",
-            "mov %rbp, 40(%rdi)",
-            "mov %rsp, 48(%rdi)",
-            "mov %rax, 56(%rdi)",
-            "mov %rcx, 64(%rdi)",
-            "",
-            "mov (%rsi), %r12",
-            "mov 8(%rsi), %r13",
-            "mov 16(%rsi), %r14",
-            "mov 24(%rsi), %r15",
-            "mov 32(%rsi), %rbx",
-            "mov 40(%rsi), %rbp",
-            "mov 48(%rsi), %rdi", // store next stack pointer
-            "mov 56(%rsi), %rax",
-            "mov 64(%rsi), %rcx",
-            "push %rcx",
-            "popf",
-            "xchg %rdi, %rsp", // switch to new stack
-            "jmp *%rax",
-            options(att_syntax),
-        );
-    }
-
     #[unsafe(naked)]
     unsafe extern "C" fn do_call() -> ! {
         naked_asm!(
@@ -111,9 +78,36 @@ impl RawTaskContext for TaskContext {
         self.rbp = 0; // NULL previous stack frame
     }
 
+    #[unsafe(naked)]
     unsafe extern "C" fn switch(from: &mut Self, to: &mut Self) {
-        unsafe {
-            Self::_switch(from, to);
-        }
+        naked_asm!(
+            "pop %rax",
+            "pushf",
+            "pop %rcx",
+            "mov %r12, (%rdi)",
+            "mov %r13, 8(%rdi)",
+            "mov %r14, 16(%rdi)",
+            "mov %r15, 24(%rdi)",
+            "mov %rbx, 32(%rdi)",
+            "mov %rbp, 40(%rdi)",
+            "mov %rsp, 48(%rdi)",
+            "mov %rax, 56(%rdi)",
+            "mov %rcx, 64(%rdi)",
+            "",
+            "mov (%rsi), %r12",
+            "mov 8(%rsi), %r13",
+            "mov 16(%rsi), %r14",
+            "mov 24(%rsi), %r15",
+            "mov 32(%rsi), %rbx",
+            "mov 40(%rsi), %rbp",
+            "mov 48(%rsi), %rdi", // store next stack pointer
+            "mov 56(%rsi), %rax",
+            "mov 64(%rsi), %rcx",
+            "push %rcx",
+            "popf",
+            "xchg %rdi, %rsp", // switch to new stack
+            "jmp *%rax",
+            options(att_syntax),
+        );
     }
 }

+ 40 - 99
crates/eonix_hal/src/arch/x86_64/cpu.rs

@@ -1,6 +1,9 @@
 use super::gdt::{GDTEntry, GDT};
 use super::interrupt::InterruptControl;
+use super::trap::TrapContext;
+use core::arch::asm;
 use core::marker::PhantomPinned;
+use core::mem::size_of;
 use core::pin::Pin;
 use eonix_preempt::PreemptGuard;
 use eonix_sync_base::LazyLock;
@@ -114,7 +117,8 @@ impl CPU {
 
     pub unsafe fn load_interrupt_stack(self: Pin<&mut Self>, rsp: u64) {
         unsafe {
-            self.map_unchecked_mut(|me| &mut me.tss).set_rsp0(rsp);
+            self.map_unchecked_mut(|me| &mut me.tss)
+                .set_rsp0(rsp + size_of::<TrapContext>() as u64);
         }
     }
 
@@ -129,7 +133,7 @@ impl CPU {
         }
 
         const IA32_KERNEL_GS_BASE: u32 = 0xc0000102;
-        arch::wrmsr(IA32_KERNEL_GS_BASE, *base);
+        wrmsr(IA32_KERNEL_GS_BASE, *base);
     }
 
     pub fn cpuid(&self) -> usize {
@@ -179,106 +183,43 @@ impl TSS {
     }
 }
 
-#[macro_export]
-macro_rules! define_smp_bootstrap {
-    ($cpu_count:literal, $ap_entry:ident, $alloc_kstack:tt) => {
-        static BOOT_SEMAPHORE: core::sync::atomic::AtomicU64 =
-            core::sync::atomic::AtomicU64::new(0);
-        static BOOT_STACK: core::sync::atomic::AtomicU64 =
-            core::sync::atomic::AtomicU64::new(0);
-
-        static CPU_COUNT: core::sync::atomic::AtomicU64 =
-            core::sync::atomic::AtomicU64::new(1);
-
-        core::arch::global_asm!(
-            r#"
-        .pushsection .stage1.smp, "ax", @progbits
-        .code16
-        .globl ap_bootstrap
-        .type ap_bootstrap, @function
-        ap_bootstrap:
-            ljmp $0x0, $2f
-
-        2:
-            # we use the shared gdt for cpu bootstrapping
-            lgdt EARLY_GDT_DESCRIPTOR
-
-            # set msr
-            mov $0xc0000080, %ecx
-            rdmsr
-            or $0x901, %eax # set LME, NXE, SCE
-            wrmsr
-
-            # set cr4
-            mov %cr4, %eax
-            or $0xa0, %eax # set PAE, PGE
-            mov %eax, %cr4
-
-            # load new page table
-            mov ${KERNEL_PML4}, %eax
-            mov %eax, %cr3
-
-            mov %cr0, %eax
-            // SET PE, WP, PG
-            or $0x80010001, %eax
-            mov %eax, %cr0
-
-            ljmp $0x08, $2f
-
-        .code64
-        2:
-            mov $0x10, %ax
-            mov %ax, %ds
-            mov %ax, %es
-            mov %ax, %ss
-
-            xor %rsp, %rsp
-            xor %rax, %rax
-            inc %rax
-        2:
-            xchg %rax, {BOOT_SEMAPHORE}
-            cmp $0, %rax
-            je 2f
-            pause
-            jmp 2b
-
-        2:
-            mov {BOOT_STACK}, %rsp # Acquire
-            cmp $0, %rsp
-            jne 2f
-            pause
-            jmp 2b
+#[inline(always)]
+pub fn halt() {
+    unsafe {
+        asm!("hlt", options(att_syntax, nostack));
+    }
+}
 
-        2:
-            xor %rax, %rax
-            mov %rax, {BOOT_STACK} # Release
-            xchg %rax, {BOOT_SEMAPHORE}
+#[inline(always)]
+pub fn rdmsr(msr: u32) -> u64 {
+    let edx: u32;
+    let eax: u32;
+
+    unsafe {
+        asm!(
+            "rdmsr",
+            in("ecx") msr,
+            out("eax") eax,
+            out("edx") edx,
+            options(att_syntax),
+        );
+    }
 
-            lock incq {CPU_COUNT}
+    (edx as u64) << 32 | eax as u64
+}
 
-            xor %rbp, %rbp
-            push %rbp # NULL return address
-            mov ${AP_ENTRY}, %rax
-            jmp *%rax
-            .popsection
-            "#,
-            KERNEL_PML4 = const 0x1000,
-            BOOT_SEMAPHORE = sym BOOT_SEMAPHORE,
-            BOOT_STACK = sym BOOT_STACK,
-            CPU_COUNT = sym CPU_COUNT,
-            AP_ENTRY = sym $ap_entry,
+#[inline(always)]
+pub fn wrmsr(msr: u32, value: u64) {
+    let eax = value as u32;
+    let edx = (value >> 32) as u32;
+
+    unsafe {
+        asm!(
+            "wrmsr",
+            in("ecx") msr,
+            in("eax") eax,
+            in("edx") edx,
             options(att_syntax),
         );
-
-        pub unsafe fn wait_cpus_online() {
-            use core::sync::atomic::Ordering;
-            while CPU_COUNT.load(Ordering::Acquire) != $cpu_count {
-                if BOOT_STACK.load(Ordering::Acquire) == 0 {
-                    let stack_bottom = $alloc_kstack as u64;
-                    BOOT_STACK.store(stack_bottom, Ordering::Release);
-                }
-                $crate::pause();
-            }
-        }
-    };
+    }
 }

+ 46 - 0
crates/eonix_hal/src/arch/x86_64/fence.rs

@@ -0,0 +1,46 @@
+use core::{
+    arch::asm,
+    sync::atomic::{compiler_fence, Ordering},
+};
+
+#[doc(hidden)]
+/// Issues a full memory barrier.
+pub fn memory_barrier() {
+    unsafe {
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+
+        asm!("mfence", options(nostack, nomem, preserves_flags));
+
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+    }
+}
+
+#[doc(hidden)]
+/// Issues a read memory barrier.
+pub fn read_memory_barrier() {
+    unsafe {
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+
+        asm!("lfence", options(nostack, nomem, preserves_flags));
+
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+    }
+}
+
+#[doc(hidden)]
+/// Issues a write memory barrier.
+pub fn write_memory_barrier() {
+    unsafe {
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+
+        asm!("sfence", options(nostack, nomem, preserves_flags));
+
+        // A full memory barrier to prevent the compiler from reordering.
+        compiler_fence(Ordering::SeqCst);
+    }
+}

+ 0 - 0
arch/src/x86_64/fpu.rs → crates/eonix_hal/src/arch/x86_64/fpu.rs


+ 3 - 3
crates/eonix_hal/src/arch/x86_64/interrupt.rs

@@ -1,4 +1,4 @@
-use arch::{pause, rdmsr};
+use crate::arch::cpu::rdmsr;
 use core::{arch::asm, marker::PhantomPinned, pin::Pin, ptr::NonNull};
 
 #[repr(C)]
@@ -166,12 +166,12 @@ impl InterruptControl {
 
         icr.write(0xc4500);
         while icr.read() & 0x1000 != 0 {
-            pause();
+            core::hint::spin_loop();
         }
 
         icr.write(0xc4606);
         while icr.read() & 0x1000 != 0 {
-            pause();
+            core::hint::spin_loop();
         }
     }
 

+ 36 - 0
crates/eonix_hal/src/arch/x86_64/io.rs

@@ -0,0 +1,36 @@
+use core::arch::asm;
+
+pub struct Port8 {
+    no: u16,
+}
+
+impl Port8 {
+    pub const fn new(no: u16) -> Self {
+        Self { no }
+    }
+
+    pub fn read(&self) -> u8 {
+        let data;
+        unsafe {
+            asm!(
+                "inb %dx, %al",
+                in("dx") self.no,
+                out("al") data,
+                options(att_syntax, nomem, nostack)
+            )
+        };
+
+        data
+    }
+
+    pub fn write(&self, data: u8) {
+        unsafe {
+            asm!(
+                "outb %al, %dx",
+                in("al") data,
+                in("dx") self.no,
+                options(att_syntax, nomem, nostack)
+            )
+        };
+    }
+}

+ 12 - 1
crates/eonix_hal/src/arch/x86_64/link.x

@@ -34,6 +34,18 @@ SECTIONS {
     } > LOWMEM AT> LOWMEM
 }
 
+SECTIONS {
+    .vdso ALIGN(0x1000) : ALIGN(0x1000)
+    {
+        KEEP(*(.vdso .vdso.*));
+
+        . = ALIGN(0x1000);
+    } > VDSO AT> REGION_TEXT
+
+    VDSO_PADDR = LOADADDR(.vdso);
+}
+INSERT AFTER .text;
+
 SECTIONS {
     .text.syscall_fns :
     {
@@ -76,7 +88,6 @@ SECTIONS {
     {
         __spercpu = .;
 
-        PERCPU_START = .;
         QUAD(0); /* Reserved for x86 percpu pointer */
 
         . = ALIGN(16);

+ 7 - 0
crates/eonix_hal/src/arch/x86_64/memory.x

@@ -1,5 +1,6 @@
 MEMORY {
     LOWMEM : org = 0x0000000000000000, len = 1M
+    VDSO   : org = 0x00007f0000000000, len = 4K
     KBSS   : org = 0xffffffffc0200000, len = 2M
     KIMAGE : org = 0xffffffffffc00000, len = 2M
 }
@@ -9,3 +10,9 @@ REGION_ALIAS("REGION_RODATA", KIMAGE);
 REGION_ALIAS("REGION_DATA", KIMAGE);
 REGION_ALIAS("REGION_BSS", KBSS);
 REGION_ALIAS("REGION_EHFRAME", KIMAGE);
+
+REGION_ALIAS("LINK_REGION_TEXT", KIMAGE);
+REGION_ALIAS("LINK_REGION_RODATA", KIMAGE);
+REGION_ALIAS("LINK_REGION_DATA", KIMAGE);
+REGION_ALIAS("LINK_REGION_BSS", KBSS);
+REGION_ALIAS("LINK_REGION_EHFRAME", KIMAGE);

+ 49 - 2
crates/eonix_hal/src/arch/x86_64/mm.rs

@@ -1,5 +1,5 @@
 use crate::traits::mm::Memory;
-use core::{marker::PhantomData, ptr::NonNull};
+use core::{arch::asm, marker::PhantomData, ptr::NonNull};
 use eonix_mm::{
     address::{Addr as _, AddrOps as _, PAddr, PRange, PhysAccess, VAddr},
     page_table::{
@@ -128,7 +128,7 @@ impl RawAttribute for PageAttribute64 {
         let mut table_attr = TableAttribute::empty();
 
         if self.0 & PA_PS != 0 {
-            panic!("Encountered a huge page while parsing table attributes");
+            return None;
         }
 
         if self.0 & PA_P != 0 {
@@ -373,3 +373,50 @@ impl Memory for ArchMemory {
         )
     }
 }
+
+#[inline(always)]
+pub fn flush_tlb(vaddr: usize) {
+    unsafe {
+        asm!(
+            "invlpg ({})",
+            in(reg) vaddr,
+            options(att_syntax)
+        );
+    }
+}
+
+#[inline(always)]
+pub fn flush_tlb_all() {
+    unsafe {
+        asm!(
+            "mov %cr3, %rax",
+            "mov %rax, %cr3",
+            out("rax") _,
+            options(att_syntax)
+        );
+    }
+}
+
+#[inline(always)]
+pub fn get_root_page_table_pfn() -> PFN {
+    let cr3: usize;
+    unsafe {
+        asm!(
+            "mov %cr3, {0}",
+            out(reg) cr3,
+            options(att_syntax)
+        );
+    }
+    PFN::from(PAddr::from(cr3))
+}
+
+#[inline(always)]
+pub fn set_root_page_table_pfn(pfn: PFN) {
+    unsafe {
+        asm!(
+            "mov {0}, %cr3",
+            in(reg) PAddr::from(pfn).addr(),
+            options(att_syntax)
+        );
+    }
+}

+ 3 - 0
crates/eonix_hal/src/arch/x86_64/mod.rs

@@ -1,7 +1,10 @@
 pub mod bootstrap;
 pub mod context;
 pub mod cpu;
+pub mod fence;
+pub mod fpu;
 pub mod gdt;
 pub mod interrupt;
+pub mod io;
 pub mod mm;
 pub mod trap;

+ 2 - 0
crates/eonix_hal/src/arch/x86_64/trap.rs

@@ -329,6 +329,8 @@ unsafe extern "C" fn captured_trap_return(trap_context: usize) -> ! {
 }
 
 impl TrapReturn for TrapContext {
+    type TaskContext = TaskContext;
+
     unsafe fn trap_return(&mut self) {
         let irq_states = disable_irqs_save();
         let old_handler = TRAP_HANDLER.swap(captured_trap_handler);

+ 48 - 4
crates/eonix_hal/src/arch/x86_64/trap/trap_context.rs

@@ -1,7 +1,9 @@
+use core::arch::asm;
 use eonix_hal_traits::{
     fault::{Fault, PageFaultErrorCode},
     trap::{RawTrapContext, TrapType},
 };
+use eonix_mm::address::VAddr;
 
 #[derive(Clone, Copy, Default)]
 #[repr(C, align(16))]
@@ -37,9 +39,6 @@ impl TrapContext {
             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;
@@ -53,7 +52,23 @@ impl TrapContext {
                     error_code |= PageFaultErrorCode::UserAccess;
                 }
 
-                Fault::PageFault(error_code)
+                #[inline(always)]
+                fn get_page_fault_address() -> VAddr {
+                    let cr2: usize;
+                    unsafe {
+                        asm!(
+                            "mov %cr2, {}",
+                            out(reg) cr2,
+                            options(att_syntax)
+                        );
+                    }
+                    VAddr::from(cr2)
+                }
+
+                Fault::PageFault {
+                    error_code,
+                    address: get_page_fault_address(),
+                }
             }
             code @ 0..0x20 => Fault::Unknown(code as usize),
             _ => unreachable!(),
@@ -127,4 +142,33 @@ impl RawTrapContext for TrapContext {
     fn set_user_return_value(&mut self, retval: usize) {
         self.rax = retval as u64;
     }
+
+    fn set_user_call_frame<E>(
+        &mut self,
+        pc: usize,
+        sp: Option<usize>,
+        ra: Option<usize>,
+        args: &[usize],
+        write_memory: impl Fn(VAddr, &[u8]) -> Result<(), E>,
+    ) -> Result<(), E> {
+        self.set_program_counter(pc);
+
+        let mut sp = sp.unwrap_or_else(|| self.get_stack_pointer());
+
+        let arg_size = args.len() * 4;
+
+        sp -= arg_size;
+        sp &= !0xf; // Align to 16 bytes
+        for (idx, arg) in args.iter().enumerate() {
+            write_memory(VAddr::from(sp + idx * 8), &arg.to_ne_bytes())?;
+        }
+
+        if let Some(ra) = ra {
+            sp -= 4; // Space for return address
+            write_memory(VAddr::from(sp), &ra.to_ne_bytes())?;
+        }
+
+        self.set_stack_pointer(sp);
+        Ok(())
+    }
 }

+ 10 - 0
crates/eonix_hal/src/bootstrap.rs

@@ -20,3 +20,13 @@ impl BootStrapData {
         self.early_stack
     }
 }
+
+#[cfg(target_arch = "riscv64")]
+pub fn early_console_write(s: &str) {
+    crate::arch::bootstrap::early_console_write(s);
+}
+
+#[cfg(target_arch = "riscv64")]
+pub fn early_console_putchar(ch: u8) {
+    crate::arch::bootstrap::early_console_putchar(ch);
+}

+ 31 - 1
crates/eonix_hal/src/lib.rs

@@ -7,8 +7,38 @@ pub(crate) mod arch;
 pub mod bootstrap;
 pub mod context;
 pub mod mm;
-pub mod processor;
 pub mod trap;
 
+pub mod fence {
+    pub use crate::arch::fence::{memory_barrier, read_memory_barrier, write_memory_barrier};
+}
+
+pub mod fpu {
+    pub use crate::arch::fpu::FpuState;
+}
+
+pub mod processor {
+    pub use crate::arch::cpu::{halt, UserTLS, CPU};
+}
+
+/// Re-export the arch module for use in other crates
+///
+/// Use this to access architecture-specific functionality in cfg wrapped blocks.
+///
+/// # Example
+/// ``` no_run
+/// #[cfg(target_arch = "x86_64")]
+/// {
+///     use eonix_hal::arch_exported::io::Port8;
+///
+///     // We know that there will be a `Port8` type available for x86_64.
+///     let port = Port8::new(0x3f8);
+///     port.write(0x01);
+/// }
+/// ```
+pub mod arch_exported {
+    pub use crate::arch::*;
+}
+
 pub use eonix_hal_macros::{ap_main, default_trap_handler, main};
 pub use eonix_hal_traits as traits;

+ 7 - 15
crates/eonix_hal/src/link.x.in

@@ -1,23 +1,15 @@
 PROVIDE(_stext = ORIGIN(REGION_TEXT));
 
 SECTIONS {
-    .text.dummy (NOLOAD) :
-    {
-        /*
-         * If we use _stext somewhere before its first appearance below, we
-         * need to define it as absolute here to avoid linker errors.
-         */
-        . = ABSOLUTE(_stext);
-
-    } > REGION_TEXT
-
     .text _stext :
     {
+        __kernel_start = .;
         __stext = .;
 
+        *(.text.entry);
         *(.text .text.*);
 
-    } > REGION_TEXT
+    } > REGION_TEXT AT> LINK_REGION_TEXT
 
     __etext = .;
 
@@ -27,7 +19,7 @@ SECTIONS {
 
         *(.rodata .rodata.*);
 
-    } > REGION_RODATA
+    } > REGION_RODATA AT> LINK_REGION_RODATA
 
     __erodata = .;
 
@@ -38,7 +30,7 @@ SECTIONS {
         *(.data .data.*);
         *(.got .got.plt);
 
-    } > REGION_DATA
+    } > REGION_DATA AT> LINK_REGION_DATA
 
     __edata = .;
 
@@ -49,7 +41,7 @@ SECTIONS {
         *(.bss .bss.*);
 
         . = ALIGN(0x1000);
-    } > REGION_BSS
+    } > REGION_BSS AT> LINK_REGION_BSS
 
     __ebss = .;
 
@@ -59,7 +51,7 @@ SECTIONS {
 
         KEEP(*(.eh_frame .eh_frame*));
 
-    } > REGION_EHFRAME
+    } > REGION_EHFRAME AT> LINK_REGION_EHFRAME
 
     . = ALIGN(0x1000);
     __eeh_frame = .;

+ 4 - 1
crates/eonix_hal/src/mm.rs

@@ -8,7 +8,10 @@ use eonix_mm::{
     paging::{PageAlloc, UnmanagedRawPage, PAGE_SIZE, PFN},
 };
 
-pub use crate::arch::mm::{ArchMemory, ArchPagingMode, ArchPhysAccess, GLOBAL_PAGE_TABLE};
+pub use crate::arch::mm::{
+    flush_tlb, flush_tlb_all, get_root_page_table_pfn, set_root_page_table_pfn, ArchMemory,
+    ArchPagingMode, ArchPhysAccess, GLOBAL_PAGE_TABLE,
+};
 
 pub struct BasicPageAlloc {
     ranges: [Option<PRange>; Self::MAX],

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

@@ -1 +0,0 @@
-pub use crate::arch::cpu::{UserTLS, CPU};

+ 4 - 0
crates/eonix_mm/src/page_table/page_table.rs

@@ -176,6 +176,10 @@ where
 
     fn drop_page_table_recursive(page_table: &Page<A>, levels: &[PageTableLevel]) {
         let [level, remaining_levels @ ..] = levels else { return };
+        if remaining_levels.is_empty() {
+            // We reached the last level, no need to go deeper.
+            return;
+        }
 
         let alloc = page_table.allocator();
 

+ 2 - 1
crates/eonix_mm/src/page_table/pte_iterator.rs

@@ -6,7 +6,7 @@ use crate::{
     address::{AddrOps as _, VRange},
     paging::{Page, PageAccess, PageAlloc},
 };
-use core::marker::PhantomData;
+use core::{marker::PhantomData};
 
 pub struct KernelIterator;
 pub struct UserIterator;
@@ -81,6 +81,7 @@ where
     X: PageAccess,
     K: IteratorType<M>,
 {
+
     fn parse_tables_starting_from(&mut self, idx_level: usize) {
         for (idx, &pt_idx) in self
             .indicies

+ 0 - 2
crates/eonix_percpu/eonix_percpu_macros/Cargo.toml

@@ -7,8 +7,6 @@ edition = "2021"
 proc-macro = true
 
 [dependencies]
-arch_macros = { path = "../../../arch/arch_macros" }
-
 proc-macro2 = "1.0"
 quote = "1.0"
 syn = { version = "2.0", features = ["full"] }

+ 28 - 5
crates/eonix_percpu/eonix_percpu_macros/src/lib.rs

@@ -1,5 +1,8 @@
 extern crate proc_macro;
 
+mod riscv64;
+mod x86_64;
+
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
 use syn::{parse2, Ident, ItemStatic, Type};
@@ -121,6 +124,7 @@ fn define_percpu_shared_impl(
     attrs: TokenStream,
     item: TokenStream,
     get_percpu_pointer: fn(&Ident, &Type) -> TokenStream,
+    get_percpu_offset: fn(&Ident) -> TokenStream,
 ) -> TokenStream {
     if !attrs.is_empty() {
         panic!("`define_percpu_shared` attribute does not take any arguments");
@@ -136,6 +140,7 @@ fn define_percpu_shared_impl(
     let access_ident = format_ident!("_access_shared_{}", ident);
 
     let as_ptr = get_percpu_pointer(&inner_ident, &ty);
+    let get_offset = get_percpu_offset(&inner_ident);
 
     quote! {
         #[link_section = ".percpu"]
@@ -156,7 +161,7 @@ fn define_percpu_shared_impl(
             }
 
             pub fn get_for_cpu(&self, cpuid: usize) -> Option<& #ty > {
-                let offset = & #inner_ident as *const _ as usize;
+                let offset = #get_offset;
                 let base = ::eonix_percpu::PercpuArea::get_for(cpuid);
                 base.map(|base| unsafe { base.byte_add(offset).cast().as_ref() })
             }
@@ -188,23 +193,41 @@ pub fn define_percpu_x86_64(
     attrs: proc_macro::TokenStream,
     item: proc_macro::TokenStream,
 ) -> proc_macro::TokenStream {
-    define_percpu_impl(
+    define_percpu_impl(attrs.into(), item.into(), x86_64::get_percpu_pointer).into()
+}
+
+#[proc_macro_attribute]
+pub fn define_percpu_shared_x86_64(
+    attrs: proc_macro::TokenStream,
+    item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    define_percpu_shared_impl(
         attrs.into(),
         item.into(),
-        arch_macros::x86_64::percpu::get_percpu_pointer,
+        x86_64::get_percpu_pointer,
+        x86_64::get_percpu_offset,
     )
     .into()
 }
 
 #[proc_macro_attribute]
-pub fn define_percpu_shared_x86_64(
+pub fn define_percpu_riscv64(
+    attrs: proc_macro::TokenStream,
+    item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    define_percpu_impl(attrs.into(), item.into(), riscv64::get_percpu_pointer).into()
+}
+
+#[proc_macro_attribute]
+pub fn define_percpu_shared_riscv64(
     attrs: proc_macro::TokenStream,
     item: proc_macro::TokenStream,
 ) -> proc_macro::TokenStream {
     define_percpu_shared_impl(
         attrs.into(),
         item.into(),
-        arch_macros::x86_64::percpu::get_percpu_pointer,
+        riscv64::get_percpu_pointer,
+        riscv64::get_percpu_offset,
     )
     .into()
 }

+ 55 - 0
crates/eonix_percpu/eonix_percpu_macros/src/riscv64.rs

@@ -0,0 +1,55 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{Ident, Type};
+
+/// Get the base address for percpu variables of the current thread.
+pub fn get_percpu_pointer(percpu: &Ident, ty: &Type) -> TokenStream {
+    quote! {
+        {
+            let base: *mut #ty;
+
+            unsafe extern "C" {
+                fn PERCPU_DATA_START();
+            }
+
+            ::core::arch::asm!(
+                "la t0, {start}",
+                "la {base}, {var}",
+                "sub {base}, {base}, t0",
+                "add {base}, {base}, tp",
+                base = out(reg) base,
+                start = sym PERCPU_DATA_START,
+                out("t0") _,
+                var = sym #percpu,
+                options(nostack, preserves_flags)
+            );
+
+            base
+        }
+    }
+}
+
+pub fn get_percpu_offset(percpu: &Ident) -> TokenStream {
+    quote! {
+        unsafe {
+            let offset: usize;
+
+            unsafe extern "C" {
+                fn PERCPU_DATA_START();
+            }
+
+            ::core::arch::asm!(
+                "la t0, {start}",
+                "la t1, {var}",
+                "sub t1, t1, t0",
+                start = sym PERCPU_DATA_START,
+                var = sym #percpu,
+                out("t0") _,
+                out("t1") offset,
+                options(nostack, preserves_flags)
+            );
+
+            offset
+        }
+    }
+}

+ 8 - 0
arch/arch_macros/src/x86_64/percpu.rs → crates/eonix_percpu/eonix_percpu_macros/src/x86_64.rs

@@ -30,3 +30,11 @@ pub fn get_percpu_pointer(percpu: &Ident, ty: &Type) -> TokenStream {
         }
     }
 }
+
+pub fn get_percpu_offset(percpu: &Ident) -> TokenStream {
+    quote! {
+        {
+            & #percpu as *const _ as usize
+        }
+    }
+}

+ 6 - 0
crates/eonix_percpu/src/lib.rs

@@ -12,6 +12,12 @@ pub use eonix_percpu_macros::define_percpu_x86_64 as define_percpu;
 #[cfg(target_arch = "x86_64")]
 pub use eonix_percpu_macros::define_percpu_shared_x86_64 as define_percpu_shared;
 
+#[cfg(target_arch = "riscv64")]
+pub use eonix_percpu_macros::define_percpu_riscv64 as define_percpu;
+
+#[cfg(target_arch = "riscv64")]
+pub use eonix_percpu_macros::define_percpu_shared_riscv64 as define_percpu_shared;
+
 const MAX_CPUS: usize = 256;
 
 #[repr(align(16))]

+ 0 - 1
crates/eonix_runtime/Cargo.toml

@@ -4,7 +4,6 @@ version = "0.1.0"
 edition = "2024"
 
 [dependencies]
-arch = { path = "../../arch" }
 atomic_unique_refcell = { path = "../atomic_unique_refcell" }
 eonix_hal = { path = "../eonix_hal" }
 eonix_log = { path = "../eonix_log" }

+ 3 - 2
crates/eonix_runtime/src/scheduler.rs

@@ -12,6 +12,7 @@ use core::{
     sync::atomic::{compiler_fence, Ordering},
     task::Waker,
 };
+use eonix_hal::processor::halt;
 use eonix_log::println_trace;
 use eonix_preempt::assert_preempt_count_eq;
 use eonix_sync::{LazyLock, Spin, SpinIrq as _};
@@ -219,7 +220,7 @@ extern "C" fn local_scheduler() -> ! {
             (None, None) => {
                 // Nothing to do, halt the cpu and rerun the loop.
                 drop(rq);
-                arch::halt();
+                halt();
                 continue;
             }
             (None, Some(next)) => {
@@ -238,7 +239,7 @@ extern "C" fn local_scheduler() -> ! {
                     // Nothing to do, halt the cpu and rerun the loop.
                     CURRENT_TASK.set(NonNull::new(Arc::into_raw(previous) as *mut _));
                     drop(rq);
-                    arch::halt();
+                    halt();
                     continue;
                 }
             }

+ 2 - 1
crates/eonix_runtime/src/task.rs

@@ -14,6 +14,7 @@ use core::{
     sync::atomic::{AtomicBool, AtomicU32, Ordering},
     task::{Context, Poll, Waker},
 };
+use eonix_hal::processor::CPU;
 use eonix_preempt::assert_preempt_enabled;
 use eonix_sync::Spin;
 use intrusive_collections::RBTreeAtomicLink;
@@ -89,7 +90,7 @@ impl Task {
             id: TaskId(ID.fetch_add(1, Ordering::Relaxed)),
             on_rq: AtomicBool::new(false),
             unparked: AtomicBool::new(false),
-            cpu: AtomicU32::new(0),
+            cpu: AtomicU32::new(CPU::local().cpuid() as u32),
             state: TaskState::new(TaskState::RUNNING),
             executor: AtomicUniqueRefCell::new(Some(executor)),
             execution_context,

+ 0 - 1
crates/eonix_sync/eonix_sync_rt/Cargo.toml

@@ -4,7 +4,6 @@ version = "0.1.0"
 edition = "2024"
 
 [dependencies]
-arch = { path = "../../../arch" }
 eonix_hal = { path = "../../eonix_hal" }
 eonix_preempt = { path = "../../eonix_preempt" }
 eonix_spin = { path = "../eonix_spin" }

+ 2 - 0
crates/posix_types/Cargo.toml

@@ -4,3 +4,5 @@ version = "0.1.0"
 edition = "2024"
 
 [dependencies]
+cfg-if = "1.0"
+bitflags = "2.6.0"

+ 29 - 0
crates/posix_types/src/constants.rs

@@ -0,0 +1,29 @@
+/// Wait for any child process
+pub const P_ALL: u32 = 0;
+
+/// Wait for a specific process by PID
+pub const P_PID: u32 = 1;
+
+/// Wait for a specific process group by PGID
+pub const P_PGID: u32 = 2;
+
+/// Wait for a specific process by PID file descriptor
+pub const P_PIDFD: u32 = 3;
+
+/// Child exited normally
+pub const CLD_EXITED: u32 = 1;
+
+/// Child was killed by a signal
+pub const CLD_KILLED: u32 = 2;
+
+/// Child terminated and dumped core
+pub const CLD_DUMPED: u32 = 3;
+
+/// Child was traced by a signal
+pub const CLD_TRAPPED: u32 = 4;
+
+/// Child was stopped by a signal
+pub const CLD_STOPPED: u32 = 5;
+
+/// Child was continued by a signal
+pub const CLD_CONTINUED: u32 = 6;

+ 55 - 0
crates/posix_types/src/ctypes.rs

@@ -0,0 +1,55 @@
+use crate::result::PosixError;
+
+#[cfg(target_arch = "x86_64")]
+pub(crate) type ArchPtrType = u32;
+
+#[cfg(not(target_arch = "x86_64"))]
+pub(crate) type ArchPtrType = u64;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct PtrT(pub(crate) ArchPtrType);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Long(pub(crate) ArchPtrType);
+
+impl PtrT {
+    pub fn new(ptr: usize) -> Result<Self, PosixError> {
+        ptr.try_into().map(Self).map_err(|_| PosixError::EFAULT)
+    }
+
+    pub const fn new_val(ptr: ArchPtrType) -> Self {
+        Self(ptr as ArchPtrType)
+    }
+
+    pub const fn null() -> Self {
+        Self(0)
+    }
+
+    pub const fn addr(self) -> usize {
+        self.0 as usize
+    }
+
+    pub const fn is_null(self) -> bool {
+        self.0 == 0
+    }
+}
+
+impl Long {
+    pub const ZERO: Self = Self(0);
+
+    pub fn new(value: usize) -> Result<Self, PosixError> {
+        value.try_into().map(Self).map_err(|_| PosixError::EINVAL)
+    }
+
+    pub const fn new_val(value: ArchPtrType) -> Self {
+        Self(value as ArchPtrType)
+    }
+
+    pub const fn zero() -> Self {
+        Self(0)
+    }
+
+    pub const fn get(self) -> usize {
+        self.0 as usize
+    }
+}

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

@@ -1,7 +1,12 @@
 #![no_std]
 
+pub mod constants;
+pub mod ctypes;
+pub mod open;
 pub mod result;
 pub mod signal;
+pub mod stat;
+pub mod syscall_no;
 
 #[cfg(target_arch = "x86_64")]
 pub mod x86_64;

+ 121 - 0
crates/posix_types/src/open.rs

@@ -0,0 +1,121 @@
+use bitflags::bitflags;
+
+bitflags! {
+    #[derive(Debug, Clone, Copy)]
+    pub struct OpenFlags: u32 {
+        /// Open for writing only
+        const O_WRONLY = 0x1;
+        /// Open for reading and writing
+        const O_RDWR = 0x2;
+        /// Create file if it does not exist
+        const O_CREAT = 0x40;
+        /// Exclusive access, fail if file exists
+        const O_EXCL = 0x80;
+        /// Truncate file to zero length if it exists
+        const O_TRUNC = 0x200;
+        /// Open file in append mode
+        const O_APPEND = 0x400;
+        /// Non-blocking mode
+        const O_NONBLOCK = 0x800;
+        /// Open directory
+        const O_DIRECTORY = 0x10000;
+        /// Do not follow symbolic links
+        const O_NOFOLLOW = 0x20000;
+        /// Close on exec
+        const O_CLOEXEC = 0x80000;
+    }
+
+    #[derive(Debug, Clone, Copy)]
+    pub struct FDFlags: u32 {
+        /// Close on exec
+        const FD_CLOEXEC = 0x1;
+    }
+
+    #[derive(Debug, Clone, Copy)]
+    pub struct AtFlags: u32 {
+        /// Do not follow symbolic links
+        const AT_SYMLINK_NOFOLLOW = 0x100;
+        /// Allow removal of directories
+        const AT_REMOVEDIR = 0x200;
+        /// Follow symbolic links when resolving paths
+        const AT_SYMLINK_FOLLOW = 0x400;
+        /// Use the file descriptor with empty path
+        const AT_EMPTY_PATH = 0x1000;
+        /// Force synchronization of file attributes
+        const AT_STATX_FORCE_SYNC = 0x2000;
+        /// Do not synchronize file attributes
+        const AT_STATX_DONT_SYNC = 0x4000;
+    }
+}
+
+impl FDFlags {
+    pub fn close_on_exec(&self) -> bool {
+        self.contains(FDFlags::FD_CLOEXEC)
+    }
+}
+
+impl OpenFlags {
+    pub fn as_fd_flags(&self) -> FDFlags {
+        if self.contains(OpenFlags::O_CLOEXEC) {
+            FDFlags::FD_CLOEXEC
+        } else {
+            FDFlags::empty()
+        }
+    }
+
+    pub fn read(&self) -> bool {
+        !self.contains(Self::O_WRONLY)
+    }
+
+    pub fn write(&self) -> bool {
+        self.intersects(Self::O_WRONLY | Self::O_RDWR)
+    }
+
+    pub fn append(&self) -> bool {
+        self.contains(Self::O_APPEND)
+    }
+
+    pub fn directory(&self) -> bool {
+        self.contains(Self::O_DIRECTORY)
+    }
+
+    pub fn truncate(&self) -> bool {
+        self.contains(Self::O_TRUNC)
+    }
+
+    pub fn follow_symlink(&self) -> bool {
+        !self.contains(Self::O_NOFOLLOW)
+    }
+
+    pub fn as_rwa(&self) -> (bool, bool, bool) {
+        (self.read(), self.write(), self.append())
+    }
+}
+
+impl AtFlags {
+    pub fn at_empty_path(&self) -> bool {
+        self.contains(AtFlags::AT_EMPTY_PATH)
+    }
+
+    /// # Notice
+    /// `no_follow` and `follow` are **DIFFERENT** and are used in different contexts.
+    ///
+    /// `follow` is used to reverse the default behavior of `linkat`, which does not
+    /// follow symlinks by default.
+    pub fn no_follow(&self) -> bool {
+        self.contains(AtFlags::AT_SYMLINK_NOFOLLOW)
+    }
+
+    /// # Notice
+    /// `no_follow` and `follow` are **DIFFERENT** and are used in different contexts.
+    ///
+    /// `follow` is used to reverse the default behavior of `linkat`, which does not
+    /// follow symlinks by default.
+    pub fn follow(&self) -> bool {
+        self.contains(AtFlags::AT_SYMLINK_FOLLOW)
+    }
+
+    pub fn statx_default_sync(&self) -> bool {
+        !self.intersects(AtFlags::AT_STATX_FORCE_SYNC | AtFlags::AT_STATX_DONT_SYNC)
+    }
+}

+ 13 - 1
crates/posix_types/src/result.rs

@@ -1 +1,13 @@
-pub enum PosixError {}
+pub enum PosixError {
+    EFAULT = 14,
+    EINVAL = 22,
+}
+
+impl From<PosixError> for u32 {
+    fn from(error: PosixError) -> Self {
+        match error {
+            PosixError::EFAULT => 14,
+            PosixError::EINVAL => 22,
+        }
+    }
+}

+ 7 - 1
crates/posix_types/src/signal.rs

@@ -1,3 +1,9 @@
 mod sig_action;
+mod siginfo;
+mod signal;
 
-pub use sig_action::{SigAction, TryFromSigAction};
+pub use sig_action::{
+    SigAction, SigActionFlags, SigActionHandler, SigActionRestorer, SigSet, TryFromSigAction,
+};
+pub use siginfo::SigInfo;
+pub use signal::Signal;

+ 136 - 42
crates/posix_types/src/signal/sig_action.rs

@@ -1,10 +1,43 @@
-#[repr(C, packed)]
+use super::Signal;
+use crate::ctypes::PtrT;
+use bitflags::bitflags;
+
+bitflags! {
+    #[derive(Debug, Clone, Copy)]
+    pub struct SigActionFlags: usize {
+        const SA_SIGINFO = 0x00000004;   // Use sa_sigaction instead of sa_handler.
+        const SA_RESTORER = 0x04000000; // Use sa_restorer to restore the context after the handler.
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct SigActionHandler(PtrT);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct SigActionRestorer(PtrT);
+
+#[cfg_attr(target_arch = "x86_64", repr(align(4)))]
+#[cfg_attr(not(target_arch = "x86_64"), repr(align(8)))]
+#[derive(Debug, Clone, Copy, Default)]
+pub struct SigSet(u64);
+
+#[cfg(target_arch = "x86_64")]
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct SigAction {
+    sa_handler: SigActionHandler,
+    sa_flags: SigActionFlags,
+    sa_restorer: SigActionRestorer,
+    sa_mask: SigSet,
+}
+
+#[cfg(not(target_arch = "x86_64"))]
+#[repr(C)]
 #[derive(Debug, Clone, Copy)]
 pub struct SigAction {
-    sa_handler: u32,
-    sa_flags: u32,
-    sa_restorer: u32,
-    sa_mask: u64,
+    sa_handler: SigActionHandler,
+    sa_flags: SigActionFlags,
+    sa_mask: SigSet,
 }
 
 pub trait TryFromSigAction: Sized {
@@ -15,61 +48,124 @@ pub trait TryFromSigAction: Sized {
     fn new() -> Self;
 
     fn set_siginfo(self) -> Result<Self, Self::Error>;
-    fn handler(self, handler: usize) -> Result<Self, Self::Error>;
-    fn restorer(self, restorer: usize) -> Result<Self, Self::Error>;
-    fn mask(self, mask: u64) -> Result<Self, Self::Error>;
+    fn handler(self, handler: SigActionHandler) -> Self;
+    fn restorer(self, restorer: SigActionRestorer) -> Self;
+    fn mask(self, mask: SigSet) -> Self;
+}
+
+impl SigActionHandler {
+    const DEFAULT: Self = Self(PtrT::new_val(0));
+    const IGNORE: Self = Self(PtrT::new_val(1));
+
+    pub const fn new(handler: PtrT) -> Self {
+        Self(handler)
+    }
+
+    pub const fn null() -> Self {
+        Self(PtrT::null())
+    }
+
+    pub const fn addr(self) -> PtrT {
+        self.0
+    }
+}
+
+impl SigActionRestorer {
+    pub const fn new(restorer: PtrT) -> Self {
+        Self(restorer)
+    }
+
+    pub const fn null() -> Self {
+        Self(PtrT::null())
+    }
+
+    pub const fn addr(self) -> PtrT {
+        self.0
+    }
 }
 
-const SIG_DFL: u32 = 0;
-const SIG_IGN: u32 = 1;
+impl SigSet {
+    pub const fn empty() -> Self {
+        Self(0)
+    }
+
+    pub fn mask(&mut self, mask: Self) {
+        self.0 |= mask.0;
+    }
+
+    pub fn unmask(&mut self, mask: Self) {
+        self.0 &= !mask.0;
+    }
+
+    pub fn include(&self, signal: Signal) -> bool {
+        let signal_mask = Self::from(signal);
+        (self.0 & signal_mask.0) != 0
+    }
+}
 
-const SA_SIGINFO: u32 = 4;
-const SA_RESTORER: u32 = 0x04000000;
+impl From<Signal> for SigSet {
+    fn from(signal: Signal) -> Self {
+        let mut sigset = Self::empty();
+        sigset.mask(Self(1 << (signal.into_raw() - 1)));
+        sigset
+    }
+}
 
 impl SigAction {
     pub const fn default() -> Self {
         Self {
-            sa_handler: SIG_DFL,
-            sa_flags: 0,
-            sa_restorer: 0,
-            sa_mask: 0,
+            sa_handler: SigActionHandler::DEFAULT,
+            sa_flags: SigActionFlags::empty(),
+            #[cfg(target_arch = "x86_64")]
+            sa_restorer: SigActionRestorer::null(),
+            sa_mask: SigSet::empty(),
         }
     }
 
     pub const fn ignore() -> Self {
         Self {
-            sa_handler: SIG_IGN,
-            sa_flags: 0,
-            sa_restorer: 0,
-            sa_mask: 0,
+            sa_handler: SigActionHandler::IGNORE,
+            sa_flags: SigActionFlags::empty(),
+            #[cfg(target_arch = "x86_64")]
+            sa_restorer: SigActionRestorer::null(),
+            sa_mask: SigSet::empty(),
         }
     }
 
     pub const fn new() -> Self {
         Self {
-            sa_handler: 0,
-            sa_flags: 0,
-            sa_restorer: 0,
-            sa_mask: 0,
+            sa_handler: SigActionHandler::null(),
+            sa_flags: SigActionFlags::empty(),
+            #[cfg(target_arch = "x86_64")]
+            sa_restorer: SigActionRestorer::null(),
+            sa_mask: SigSet::empty(),
         }
     }
 
-    pub const fn handler(self, handler: usize) -> Self {
+    pub fn handler(self, handler: SigActionHandler) -> Self {
         Self {
-            sa_handler: handler as u32,
+            sa_handler: handler,
             ..self
         }
     }
 
-    pub const fn restorer(self, restorer: usize) -> Self {
+    #[cfg(not(target_arch = "x86_64"))]
+    pub fn restorer(self, _restorer: SigActionRestorer) -> Self {
+        // On non-x86_64 architectures, the restorer does not exist.
+        self
+    }
+
+    #[cfg(target_arch = "x86_64")]
+    pub fn restorer(mut self, restorer: SigActionRestorer) -> Self {
+        self.sa_flags.insert(SigActionFlags::SA_RESTORER);
+
         Self {
-            sa_restorer: restorer as u32,
-            sa_flags: self.sa_flags | SA_RESTORER,
+            sa_restorer: restorer,
             ..self
         }
     }
 
-    pub const fn mask(self, mask: u64) -> Self {
+    pub const fn mask(self, mask: SigSet) -> Self {
         Self {
             sa_mask: mask,
             ..self
@@ -81,26 +177,24 @@ impl SigAction {
         T: TryFromSigAction,
     {
         match self.sa_handler {
-            SIG_DFL => Ok(T::default()),
-            SIG_IGN => Ok(T::ignore()),
+            SigActionHandler::DEFAULT => Ok(T::default()),
+            SigActionHandler::IGNORE => Ok(T::ignore()),
             _ => {
                 let mut action = T::new();
-                if self.sa_flags & SA_SIGINFO != 0 {
+                if self.sa_flags.contains(SigActionFlags::SA_SIGINFO) {
                     action = action.set_siginfo()?;
                 }
 
-                action = action.handler(self.sa_handler as usize)?;
-                action = action.restorer(self.sa_restorer as usize)?;
-                action = action.mask(self.sa_mask)?;
+                action = action.handler(self.sa_handler);
+                action = action.mask(self.sa_mask);
+
+                #[cfg(target_arch = "x86_64")]
+                {
+                    action = action.restorer(self.sa_restorer);
+                }
 
                 Ok(action)
             }
         }
     }
 }
-
-impl Default for SigAction {
-    fn default() -> Self {
-        Self::default()
-    }
-}

+ 28 - 0
crates/posix_types/src/signal/siginfo.rs

@@ -0,0 +1,28 @@
+#[repr(C)]
+#[derive(Default, Clone, Copy)]
+pub struct SigInfo {
+    pub si_signo: u32,       // Signal number
+    pub si_errno: u32,       // Error number
+    pub si_code: u32,        // Signal code
+    pub si_trapno: u32,      // Trap number that caused the signal (unused)
+    pub si_pid: u32,         // Sending process ID
+    pub si_uid: u32,         // Sending user ID
+    pub si_status: u32,      // Exit status or signal
+    pub si_utime: u64,       // User time consumed
+    pub si_stime: u64,       // System time consumed
+    pub si_value: u64,       // Signal value (union sigval)
+    pub si_int: u32,         // Integer value
+    pub si_ptr: usize,       // Pointer value
+    pub si_overrun: u32,     // Timer overrun count
+    pub si_timerid: u32,     // Timer ID (POSIX.1b timers)
+    pub si_addr: usize,      // Address that caused the fault
+    pub si_band: u64,        // Band event for SIGPOLL
+    pub si_fd: u32,          // File descriptor
+    pub si_addr_lsb: u16,    // Least significant bit of address
+    pub si_lower: usize,     // Lower bound when address violation occurred
+    pub si_upper: usize,     // Upper bound when address violation occurred
+    pub si_pkey: u32,        // Protection key on PTE that caused fault
+    pub si_call_addr: usize, // Address of system call instruction
+    pub si_syscall: u32,     // Number of attempted system call
+    pub si_arch: u32,        // Architecture of attempted system call
+}

+ 138 - 0
crates/posix_types/src/signal/signal.rs

@@ -0,0 +1,138 @@
+use crate::result::PosixError;
+use core::fmt;
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Signal(u32);
+
+impl Signal {
+    pub const SIGHUP: Signal = Signal(1);
+    pub const SIGINT: Signal = Signal(2);
+    pub const SIGQUIT: Signal = Signal(3);
+    pub const SIGILL: Signal = Signal(4);
+    pub const SIGTRAP: Signal = Signal(5);
+    pub const SIGABRT: Signal = Signal(6);
+    pub const SIGIOT: Signal = Signal(6);
+    pub const SIGBUS: Signal = Signal(7);
+    pub const SIGFPE: Signal = Signal(8);
+    pub const SIGKILL: Signal = Signal(9);
+    pub const SIGUSR1: Signal = Signal(10);
+    pub const SIGSEGV: Signal = Signal(11);
+    pub const SIGUSR2: Signal = Signal(12);
+    pub const SIGPIPE: Signal = Signal(13);
+    pub const SIGALRM: Signal = Signal(14);
+    pub const SIGTERM: Signal = Signal(15);
+    pub const SIGSTKFLT: Signal = Signal(16);
+    pub const SIGCHLD: Signal = Signal(17);
+    pub const SIGCONT: Signal = Signal(18);
+    pub const SIGSTOP: Signal = Signal(19);
+    pub const SIGTSTP: Signal = Signal(20);
+    pub const SIGTTIN: Signal = Signal(21);
+    pub const SIGTTOU: Signal = Signal(22);
+    pub const SIGURG: Signal = Signal(23);
+    pub const SIGXCPU: Signal = Signal(24);
+    pub const SIGXFSZ: Signal = Signal(25);
+    pub const SIGVTALRM: Signal = Signal(26);
+    pub const SIGPROF: Signal = Signal(27);
+    pub const SIGWINCH: Signal = Signal(28);
+    pub const SIGIO: Signal = Signal(29);
+    pub const SIGPOLL: Signal = Signal(29);
+    pub const SIGPWR: Signal = Signal(30);
+    pub const SIGSYS: Signal = Signal(31);
+
+    pub const SIGNUM_MIN: u32 = 1;
+    pub const SIGNUM_MAX: u32 = 64;
+
+    pub fn into_raw(self) -> u32 {
+        self.0
+    }
+
+    pub fn try_from_raw(signo: u32) -> Result<Self, PosixError> {
+        match signo {
+            ..Self::SIGNUM_MAX => Ok(Signal(signo)),
+            _ => Err(PosixError::EINVAL),
+        }
+    }
+}
+
+impl fmt::Debug for Signal {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            &Signal(0) => write!(f, "Signal::EMPTY"),
+            &Signal::SIGHUP => write!(f, "SIGHUP"),
+            &Signal::SIGINT => write!(f, "SIGINT"),
+            &Signal::SIGQUIT => write!(f, "SIGQUIT"),
+            &Signal::SIGILL => write!(f, "SIGILL"),
+            &Signal::SIGTRAP => write!(f, "SIGTRAP"),
+            &Signal::SIGABRT => write!(f, "SIGABRT"),
+            &Signal::SIGBUS => write!(f, "SIGBUS"),
+            &Signal::SIGFPE => write!(f, "SIGFPE"),
+            &Signal::SIGKILL => write!(f, "SIGKILL"),
+            &Signal::SIGUSR1 => write!(f, "SIGUSR1"),
+            &Signal::SIGSEGV => write!(f, "SIGSEGV"),
+            &Signal::SIGUSR2 => write!(f, "SIGUSR2"),
+            &Signal::SIGPIPE => write!(f, "SIGPIPE"),
+            &Signal::SIGALRM => write!(f, "SIGALRM"),
+            &Signal::SIGTERM => write!(f, "SIGTERM"),
+            &Signal::SIGSTKFLT => write!(f, "SIGSTKFLT"),
+            &Signal::SIGCHLD => write!(f, "SIGCHLD"),
+            &Signal::SIGCONT => write!(f, "SIGCONT"),
+            &Signal::SIGSTOP => write!(f, "SIGSTOP"),
+            &Signal::SIGTSTP => write!(f, "SIGTSTP"),
+            &Signal::SIGTTIN => write!(f, "SIGTTIN"),
+            &Signal::SIGTTOU => write!(f, "SIGTTOU"),
+            &Signal::SIGURG => write!(f, "SIGURG"),
+            &Signal::SIGXCPU => write!(f, "SIGXCPU"),
+            &Signal::SIGXFSZ => write!(f, "SIGXFSZ"),
+            &Signal::SIGVTALRM => write!(f, "SIGVTALRM"),
+            &Signal::SIGPROF => write!(f, "SIGPROF"),
+            &Signal::SIGWINCH => write!(f, "SIGWINCH"),
+            &Signal::SIGIO => write!(f, "SIGNOIO/SIGPOLL"),
+            &Signal::SIGPWR => write!(f, "SIGHUPWR"),
+            &Signal::SIGSYS => write!(f, "SIGSYS"),
+            &Signal(signo @ ..Signal::SIGNUM_MAX) => write!(f, "SIGUSER{}", signo),
+            &Signal(signo) => write!(f, "Signal::UNKNOWN({})", signo),
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! SIGNAL_IGNORE {
+    () => {
+        $crate::signal::Signal::SIGCHLD
+            | $crate::signal::Signal::SIGURG
+            | $crate::signal::Signal::SIGWINCH
+    };
+}
+
+#[macro_export]
+macro_rules! SIGNAL_NOW {
+    () => {
+        $crate::signal::Signal::SIGKILL | $crate::signal::Signal::SIGSTOP
+    };
+}
+
+#[macro_export]
+macro_rules! SIGNAL_COREDUMP {
+    () => {
+        $crate::signal::Signal::SIGQUIT
+            | $crate::signal::Signal::SIGILL
+            | $crate::signal::Signal::SIGABRT
+            | $crate::signal::Signal::SIGFPE
+            | $crate::signal::Signal::SIGSEGV
+            | $crate::signal::Signal::SIGBUS
+            | $crate::signal::Signal::SIGTRAP
+            | $crate::signal::Signal::SIGSYS
+            | $crate::signal::Signal::SIGXCPU
+            | $crate::signal::Signal::SIGXFSZ
+    };
+}
+
+#[macro_export]
+macro_rules! SIGNAL_STOP {
+    () => {
+        $crate::signal::Signal::SIGSTOP
+            | $crate::signal::Signal::SIGTSTP
+            | $crate::signal::Signal::SIGTTIN
+            | $crate::signal::Signal::SIGTTOU
+    };
+}

+ 96 - 0
crates/posix_types/src/stat.rs

@@ -0,0 +1,96 @@
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct StatXTimestamp {
+    pub tv_sec: i64,
+    pub tv_nsec: u32,
+    pub __reserved: i32,
+}
+
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct StatX {
+    pub stx_mask: u32,
+    pub stx_blksize: u32,
+    pub stx_attributes: u64,
+    pub stx_nlink: u32,
+    pub stx_uid: u32,
+    pub stx_gid: u32,
+    pub stx_mode: u16,
+    pub __spare0: [u16; 1usize],
+    pub stx_ino: u64,
+    pub stx_size: u64,
+    pub stx_blocks: u64,
+    pub stx_attributes_mask: u64,
+    pub stx_atime: StatXTimestamp,
+    pub stx_btime: StatXTimestamp,
+    pub stx_ctime: StatXTimestamp,
+    pub stx_mtime: StatXTimestamp,
+    pub stx_rdev_major: u32,
+    pub stx_rdev_minor: u32,
+    pub stx_dev_major: u32,
+    pub stx_dev_minor: u32,
+    pub stx_mnt_id: u64,
+    pub stx_dio_alignment: [u64; 13usize],
+}
+
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct TimeSpec {
+    pub tv_sec: u64,
+    pub tv_nsec: u64,
+}
+
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct Stat {
+    pub st_dev: u64,
+    pub st_ino: u64,
+    pub st_mode: u32,
+    pub st_nlink: usize,
+    pub st_uid: u32,
+    pub st_gid: u32,
+    pub st_rdev: u64,
+    __padding: usize,
+
+    pub st_size: u64,
+    pub st_blksize: usize,
+    __padding2: u32,
+
+    pub st_blocks: u64,
+    pub st_atime: TimeSpec,
+    pub st_mtime: TimeSpec,
+    pub st_ctime: TimeSpec,
+}
+
+impl From<StatX> for Stat {
+    fn from(statx: StatX) -> Self {
+        Self {
+            st_dev: statx.stx_dev_major as u64 | ((statx.stx_dev_minor as u64) << 32),
+            st_ino: statx.stx_ino,
+            st_mode: statx.stx_mode as u32,
+            st_nlink: statx.stx_nlink as usize,
+            st_uid: statx.stx_uid,
+            st_gid: statx.stx_gid,
+            st_rdev: statx.stx_rdev_major as u64 | ((statx.stx_rdev_minor as u64) << 32),
+            __padding: 0,
+
+            st_size: statx.stx_size,
+            st_blksize: statx.stx_blksize as usize,
+            __padding2: 0,
+
+            st_blocks: statx.stx_blocks,
+            st_atime: TimeSpec {
+                tv_sec: statx.stx_atime.tv_sec as u64,
+                tv_nsec: statx.stx_atime.tv_nsec as u64,
+            },
+            st_mtime: TimeSpec {
+                tv_sec: statx.stx_mtime.tv_sec as u64,
+                tv_nsec: statx.stx_mtime.tv_nsec as u64,
+            },
+            st_ctime: TimeSpec {
+                tv_sec: statx.stx_ctime.tv_sec as u64,
+                tv_nsec: statx.stx_ctime.tv_nsec as u64,
+            },
+        }
+    }
+}

+ 13 - 0
crates/posix_types/src/syscall_no.rs

@@ -0,0 +1,13 @@
+use cfg_if::cfg_if;
+
+cfg_if! {
+    if #[cfg(target_arch = "riscv64")] {
+        mod riscv64;
+        pub use riscv64::*;
+    } else if #[cfg(target_arch = "x86_64")] {
+        mod x86_64;
+        pub use x86_64::*;
+    } else {
+        compile_error!("Unsupported architecture for syscall numbers");
+    }
+}

+ 297 - 0
crates/posix_types/src/syscall_no/riscv64.rs

@@ -0,0 +1,297 @@
+// RISC-V 64-bit syscall numbers
+pub const SYS_IO_SETUP: usize = 0;
+pub const SYS_IO_DESTROY: usize = 1;
+pub const SYS_IO_SUBMIT: usize = 2;
+pub const SYS_IO_CANCEL: usize = 3;
+pub const SYS_IO_GETEVENTS: usize = 4;
+pub const SYS_SETXATTR: usize = 5;
+pub const SYS_LSETXATTR: usize = 6;
+pub const SYS_FSETXATTR: usize = 7;
+pub const SYS_GETXATTR: usize = 8;
+pub const SYS_LGETXATTR: usize = 9;
+pub const SYS_FGETXATTR: usize = 10;
+pub const SYS_LISTXATTR: usize = 11;
+pub const SYS_LLISTXATTR: usize = 12;
+pub const SYS_FLISTXATTR: usize = 13;
+pub const SYS_REMOVEXATTR: usize = 14;
+pub const SYS_LREMOVEXATTR: usize = 15;
+pub const SYS_FREMOVEXATTR: usize = 16;
+pub const SYS_GETCWD: usize = 17;
+pub const SYS_LOOKUP_DCOOKIE: usize = 18;
+pub const SYS_EVENTFD2: usize = 19;
+pub const SYS_EPOLL_CREATE1: usize = 20;
+pub const SYS_EPOLL_CTL: usize = 21;
+pub const SYS_EPOLL_PWAIT: usize = 22;
+pub const SYS_DUP: usize = 23;
+pub const SYS_DUP3: usize = 24;
+pub const SYS_FCNTL64: usize = 25;
+pub const SYS_INOTIFY_INIT1: usize = 26;
+pub const SYS_INOTIFY_ADD_WATCH: usize = 27;
+pub const SYS_INOTIFY_RM_WATCH: usize = 28;
+pub const SYS_IOCTL: usize = 29;
+pub const SYS_IOPRIO_SET: usize = 30;
+pub const SYS_IOPRIO_GET: usize = 31;
+pub const SYS_FLOCK: usize = 32;
+pub const SYS_MKNODAT: usize = 33;
+pub const SYS_MKDIRAT: usize = 34;
+pub const SYS_UNLINKAT: usize = 35;
+pub const SYS_SYMLINKAT: usize = 36;
+pub const SYS_LINKAT: usize = 37;
+pub const SYS_RENAMEAT: usize = 38;
+pub const SYS_UMOUNT: usize = 39;
+pub const SYS_MOUNT: usize = 40;
+pub const SYS_PIVOT_ROOT: usize = 41;
+pub const SYS_NI_SYSCALL: usize = 42;
+pub const SYS_STATFS64: usize = 43;
+pub const SYS_FSTATFS64: usize = 44;
+pub const SYS_TRUNCATE64: usize = 45;
+pub const SYS_FTRUNCATE64: usize = 46;
+pub const SYS_FALLOCATE: usize = 47;
+pub const SYS_FACCESSAT: usize = 48;
+pub const SYS_CHDIR: usize = 49;
+pub const SYS_FCHDIR: usize = 50;
+pub const SYS_CHROOT: usize = 51;
+pub const SYS_FCHMOD: usize = 52;
+pub const SYS_FCHMODAT: usize = 53;
+pub const SYS_FCHOWNAT: usize = 54;
+pub const SYS_FCHOWN: usize = 55;
+pub const SYS_OPENAT: usize = 56;
+pub const SYS_CLOSE: usize = 57;
+pub const SYS_VHANGUP: usize = 58;
+pub const SYS_PIPE2: usize = 59;
+pub const SYS_QUOTACTL: usize = 60;
+pub const SYS_GETDENTS64: usize = 61;
+pub const SYS_LSEEK: usize = 62;
+pub const SYS_READ: usize = 63;
+pub const SYS_WRITE: usize = 64;
+pub const SYS_READV: usize = 65;
+pub const SYS_WRITEV: usize = 66;
+pub const SYS_PREAD64: usize = 67;
+pub const SYS_PWRITE64: usize = 68;
+pub const SYS_PREADV: usize = 69;
+pub const SYS_PWRITEV: usize = 70;
+pub const SYS_SENDFILE64: usize = 71;
+pub const SYS_PSELECT6_TIME32: usize = 72;
+pub const SYS_PPOLL: usize = 73;
+pub const SYS_SIGNALFD4: usize = 74;
+pub const SYS_VMSPLICE: usize = 75;
+pub const SYS_SPLICE: usize = 76;
+pub const SYS_TEE: usize = 77;
+pub const SYS_READLINKAT: usize = 78;
+pub const SYS_NEWFSTATAT: usize = 79;
+pub const SYS_NEWFSTAT: usize = 80;
+pub const SYS_SYNC: usize = 81;
+pub const SYS_FSYNC: usize = 82;
+pub const SYS_FDATASYNC: usize = 83;
+pub const SYS_SYNC_FILE_RANGE2: usize = 84;
+pub const SYS_SYNC_FILE_RANGE: usize = 84;
+pub const SYS_TIMERFD_CREATE: usize = 85;
+pub const SYS_TIMERFD_SETTIME: usize = 411;
+pub const SYS_TIMERFD_GETTIME: usize = 410;
+pub const SYS_UTIMENSAT: usize = 412;
+pub const SYS_ACCT: usize = 89;
+pub const SYS_CAPGET: usize = 90;
+pub const SYS_CAPSET: usize = 91;
+pub const SYS_PERSONALITY: usize = 92;
+pub const SYS_EXIT: usize = 93;
+pub const SYS_EXIT_GROUP: usize = 94;
+pub const SYS_WAITID: usize = 95;
+pub const SYS_SET_TID_ADDRESS: usize = 96;
+pub const SYS_UNSHARE: usize = 97;
+pub const SYS_FUTEX: usize = 422;
+pub const SYS_SET_ROBUST_LIST: usize = 99;
+pub const SYS_GET_ROBUST_LIST: usize = 100;
+pub const SYS_NANOSLEEP: usize = 101;
+pub const SYS_GETITIMER: usize = 102;
+pub const SYS_SETITIMER: usize = 103;
+pub const SYS_KEXEC_LOAD: usize = 104;
+pub const SYS_INIT_MODULE: usize = 105;
+pub const SYS_DELETE_MODULE: usize = 106;
+pub const SYS_TIMER_CREATE: usize = 107;
+pub const SYS_TIMER_GETOVERRUN: usize = 109;
+pub const SYS_TIMER_SETTIME: usize = 409;
+pub const SYS_TIMER_DELETE: usize = 111;
+pub const SYS_CLOCK_SETTIME: usize = 404;
+pub const SYS_CLOCK_GETTIME: usize = 113;
+pub const SYS_CLOCK_GETRES: usize = 406;
+pub const SYS_CLOCK_NANOSLEEP: usize = 407;
+pub const SYS_SYSLOG: usize = 116;
+pub const SYS_PTRACE: usize = 117;
+pub const SYS_SCHED_SETPARAM: usize = 118;
+pub const SYS_SCHED_SETSCHEDULER: usize = 119;
+pub const SYS_SCHED_GETSCHEDULER: usize = 120;
+pub const SYS_SCHED_GETPARAM: usize = 121;
+pub const SYS_SCHED_SETAFFINITY: usize = 122;
+pub const SYS_SCHED_GETAFFINITY: usize = 123;
+pub const SYS_SCHED_YIELD: usize = 124;
+pub const SYS_SCHED_GET_PRIORITY_MAX: usize = 125;
+pub const SYS_SCHED_GET_PRIORITY_MIN: usize = 126;
+pub const SYS_SCHED_RR_GET_INTERVAL: usize = 423;
+pub const SYS_RESTART_SYSCALL: usize = 128;
+pub const SYS_KILL: usize = 129;
+pub const SYS_TKILL: usize = 130;
+pub const SYS_TGKILL: usize = 131;
+pub const SYS_SIGALTSTACK: usize = 132;
+pub const SYS_RT_SIGSUSPEND: usize = 133;
+pub const SYS_RT_SIGACTION: usize = 134;
+pub const SYS_RT_SIGPROCMASK: usize = 135;
+pub const SYS_RT_SIGPENDING: usize = 136;
+pub const SYS_RT_SIGTIMEDWAIT_TIME32: usize = 137;
+pub const SYS_RT_SIGQUEUEINFO: usize = 138;
+pub const SYS_RT_SIGRETURN: usize = 139;
+pub const SYS_SETPRIORITY: usize = 140;
+pub const SYS_GETPRIORITY: usize = 141;
+pub const SYS_REBOOT: usize = 142;
+pub const SYS_SETREGID: usize = 143;
+pub const SYS_SETGID: usize = 144;
+pub const SYS_SETREUID: usize = 145;
+pub const SYS_SETUID: usize = 146;
+pub const SYS_SETRESUID: usize = 147;
+pub const SYS_GETRESUID: usize = 148;
+pub const SYS_SETRESGID: usize = 149;
+pub const SYS_GETRESGID: usize = 150;
+pub const SYS_SETFSUID: usize = 151;
+pub const SYS_SETFSGID: usize = 152;
+pub const SYS_TIMES: usize = 153;
+pub const SYS_SETPGID: usize = 154;
+pub const SYS_GETPGID: usize = 155;
+pub const SYS_GETSID: usize = 156;
+pub const SYS_SETSID: usize = 157;
+pub const SYS_GETGROUPS: usize = 158;
+pub const SYS_SETGROUPS: usize = 159;
+pub const SYS_NEWUNAME: usize = 160;
+pub const SYS_SETHOSTNAME: usize = 161;
+pub const SYS_SETDOMAINNAME: usize = 162;
+pub const SYS_GETRLIMIT: usize = 163;
+pub const SYS_SETRLIMIT: usize = 164;
+pub const SYS_GETRUSAGE: usize = 165;
+pub const SYS_UMASK: usize = 166;
+pub const SYS_PRCTL: usize = 167;
+pub const SYS_GETCPU: usize = 168;
+pub const SYS_GETTIMEOFDAY: usize = 169;
+pub const SYS_SETTIMEOFDAY: usize = 170;
+pub const SYS_ADJTIMEX: usize = 171;
+pub const SYS_GETPID: usize = 172;
+pub const SYS_GETPPID: usize = 173;
+pub const SYS_GETUID: usize = 174;
+pub const SYS_GETEUID: usize = 175;
+pub const SYS_GETGID: usize = 176;
+pub const SYS_GETEGID: usize = 177;
+pub const SYS_GETTID: usize = 178;
+pub const SYS_SYSINFO: usize = 179;
+pub const SYS_MQ_OPEN: usize = 180;
+pub const SYS_MQ_UNLINK: usize = 181;
+pub const SYS_MQ_TIMEDSEND: usize = 418;
+pub const SYS_MQ_TIMEDRECEIVE: usize = 419;
+pub const SYS_MQ_NOTIFY: usize = 184;
+pub const SYS_MQ_GETSETATTR: usize = 185;
+pub const SYS_MSGGET: usize = 186;
+pub const SYS_MSGCTL: usize = 187;
+pub const SYS_MSGRCV: usize = 188;
+pub const SYS_MSGSND: usize = 189;
+pub const SYS_SEMGET: usize = 190;
+pub const SYS_SEMCTL: usize = 191;
+pub const SYS_SEMTIMEDOP: usize = 420;
+pub const SYS_SEMOP: usize = 193;
+pub const SYS_SHMGET: usize = 194;
+pub const SYS_SHMCTL: usize = 195;
+pub const SYS_SHMAT: usize = 196;
+pub const SYS_SHMDT: usize = 197;
+pub const SYS_SOCKET: usize = 198;
+pub const SYS_SOCKETPAIR: usize = 199;
+pub const SYS_BIND: usize = 200;
+pub const SYS_LISTEN: usize = 201;
+pub const SYS_ACCEPT: usize = 202;
+pub const SYS_CONNECT: usize = 203;
+pub const SYS_GETSOCKNAME: usize = 204;
+pub const SYS_GETPEERNAME: usize = 205;
+pub const SYS_SENDTO: usize = 206;
+pub const SYS_RECVFROM: usize = 207;
+pub const SYS_SETSOCKOPT: usize = 208;
+pub const SYS_GETSOCKOPT: usize = 209;
+pub const SYS_SHUTDOWN: usize = 210;
+pub const SYS_SENDMSG: usize = 211;
+pub const SYS_RECVMSG: usize = 212;
+pub const SYS_READAHEAD: usize = 213;
+pub const SYS_BRK: usize = 214;
+pub const SYS_MUNMAP: usize = 215;
+pub const SYS_MREMAP: usize = 216;
+pub const SYS_ADD_KEY: usize = 217;
+pub const SYS_REQUEST_KEY: usize = 218;
+pub const SYS_KEYCTL: usize = 219;
+pub const SYS_CLONE: usize = 220;
+pub const SYS_EXECVE: usize = 221;
+pub const SYS_MMAP: usize = 222;
+pub const SYS_FADVISE64_64: usize = 223;
+pub const SYS_SWAPON: usize = 224;
+pub const SYS_SWAPOFF: usize = 225;
+pub const SYS_MPROTECT: usize = 226;
+pub const SYS_MSYNC: usize = 227;
+pub const SYS_MLOCK: usize = 228;
+pub const SYS_MUNLOCK: usize = 229;
+pub const SYS_MLOCKALL: usize = 230;
+pub const SYS_MUNLOCKALL: usize = 231;
+pub const SYS_MINCORE: usize = 232;
+pub const SYS_MADVISE: usize = 233;
+pub const SYS_REMAP_FILE_PAGES: usize = 234;
+pub const SYS_MBIND: usize = 235;
+pub const SYS_GET_MEMPOLICY: usize = 236;
+pub const SYS_SET_MEMPOLICY: usize = 237;
+pub const SYS_MIGRATE_PAGES: usize = 238;
+pub const SYS_MOVE_PAGES: usize = 239;
+pub const SYS_RT_TGSIGQUEUEINFO: usize = 240;
+pub const SYS_PERF_EVENT_OPEN: usize = 241;
+pub const SYS_ACCEPT4: usize = 242;
+pub const SYS_RECVMMSG_TIME32: usize = 243;
+pub const SYS_WAIT4: usize = 260;
+pub const SYS_PRLIMIT64: usize = 261;
+pub const SYS_FANOTIFY_INIT: usize = 262;
+pub const SYS_FANOTIFY_MARK: usize = 263;
+pub const SYS_NAME_TO_HANDLE_AT: usize = 264;
+pub const SYS_OPEN_BY_HANDLE_AT: usize = 265;
+pub const SYS_CLOCK_ADJTIME: usize = 405;
+pub const SYS_SYNCFS: usize = 267;
+pub const SYS_SETNS: usize = 268;
+pub const SYS_SENDMMSG: usize = 269;
+pub const SYS_PROCESS_VM_READV: usize = 270;
+pub const SYS_PROCESS_VM_WRITEV: usize = 271;
+pub const SYS_KCMP: usize = 272;
+pub const SYS_FINIT_MODULE: usize = 273;
+pub const SYS_SCHED_SETATTR: usize = 274;
+pub const SYS_SCHED_GETATTR: usize = 275;
+pub const SYS_RENAMEAT2: usize = 276;
+pub const SYS_SECCOMP: usize = 277;
+pub const SYS_GETRANDOM: usize = 278;
+pub const SYS_MEMFD_CREATE: usize = 279;
+pub const SYS_BPF: usize = 280;
+pub const SYS_EXECVEAT: usize = 281;
+pub const SYS_USERFAULTFD: usize = 282;
+pub const SYS_MEMBARRIER: usize = 283;
+pub const SYS_MLOCK2: usize = 284;
+pub const SYS_COPY_FILE_RANGE: usize = 285;
+pub const SYS_PREADV2: usize = 286;
+pub const SYS_PWRITEV2: usize = 287;
+pub const SYS_PKEY_MPROTECT: usize = 288;
+pub const SYS_PKEY_ALLOC: usize = 289;
+pub const SYS_PKEY_FREE: usize = 290;
+pub const SYS_STATX: usize = 291;
+pub const SYS_IO_PGETEVENTS: usize = 416;
+pub const SYS_RSEQ: usize = 293;
+pub const SYS_KEXEC_FILE_LOAD: usize = 294;
+pub const SYS_PIDFD_SEND_SIGNAL: usize = 424;
+pub const SYS_IO_URING_SETUP: usize = 425;
+pub const SYS_IO_URING_ENTER: usize = 426;
+pub const SYS_IO_URING_REGISTER: usize = 427;
+pub const SYS_OPEN_TREE: usize = 428;
+pub const SYS_MOVE_MOUNT: usize = 429;
+pub const SYS_FSOPEN: usize = 430;
+pub const SYS_FSCONFIG: usize = 431;
+pub const SYS_FSMOUNT: usize = 432;
+pub const SYS_FSPICK: usize = 433;
+pub const SYS_PIDFD_OPEN: usize = 434;
+pub const SYS_CLONE3: usize = 435;
+pub const SYS_CLOSE_RANGE: usize = 436;
+pub const SYS_OPENAT2: usize = 437;
+pub const SYS_PIDFD_GETFD: usize = 438;
+pub const SYS_FACCESSAT2: usize = 439;
+pub const SYS_PROCESS_MADVISE: usize = 440;

+ 433 - 0
crates/posix_types/src/syscall_no/x86_64.rs

@@ -0,0 +1,433 @@
+// i386 (32-bit x86) syscall numbers - used even on x86_64 builds
+pub const SYS_RESTART_SYSCALL: usize = 0;
+pub const SYS_EXIT: usize = 1;
+pub const SYS_FORK: usize = 2;
+pub const SYS_READ: usize = 3;
+pub const SYS_WRITE: usize = 4;
+pub const SYS_OPEN: usize = 5;
+pub const SYS_CLOSE: usize = 6;
+pub const SYS_WAITPID: usize = 7;
+pub const SYS_CREAT: usize = 8;
+pub const SYS_LINK: usize = 9;
+pub const SYS_UNLINK: usize = 10;
+pub const SYS_EXECVE: usize = 11;
+pub const SYS_CHDIR: usize = 12;
+pub const SYS_TIME: usize = 13;
+pub const SYS_MKNOD: usize = 14;
+pub const SYS_CHMOD: usize = 15;
+pub const SYS_LCHOWN: usize = 16;
+pub const SYS_BREAK: usize = 17;
+pub const SYS_OLDSTAT: usize = 18;
+pub const SYS_LSEEK: usize = 19;
+pub const SYS_GETPID: usize = 20;
+pub const SYS_MOUNT: usize = 21;
+pub const SYS_UMOUNT: usize = 22;
+pub const SYS_SETUID: usize = 23;
+pub const SYS_GETUID: usize = 24;
+pub const SYS_STIME: usize = 25;
+pub const SYS_PTRACE: usize = 26;
+pub const SYS_ALARM: usize = 27;
+pub const SYS_OLDFSTAT: usize = 28;
+pub const SYS_PAUSE: usize = 29;
+pub const SYS_UTIME: usize = 30;
+pub const SYS_STTY: usize = 31;
+pub const SYS_GTTY: usize = 32;
+pub const SYS_ACCESS: usize = 33;
+pub const SYS_NICE: usize = 34;
+pub const SYS_FTIME: usize = 35;
+pub const SYS_SYNC: usize = 36;
+pub const SYS_KILL: usize = 37;
+pub const SYS_RENAME: usize = 38;
+pub const SYS_MKDIR: usize = 39;
+pub const SYS_RMDIR: usize = 40;
+pub const SYS_DUP: usize = 41;
+pub const SYS_PIPE: usize = 42;
+pub const SYS_TIMES: usize = 43;
+pub const SYS_PROF: usize = 44;
+pub const SYS_BRK: usize = 45;
+pub const SYS_SETGID: usize = 46;
+pub const SYS_GETGID: usize = 47;
+pub const SYS_SIGNAL: usize = 48;
+pub const SYS_GETEUID: usize = 49;
+pub const SYS_GETEGID: usize = 50;
+pub const SYS_ACCT: usize = 51;
+pub const SYS_UMOUNT2: usize = 52;
+pub const SYS_LOCK: usize = 53;
+pub const SYS_IOCTL: usize = 54;
+pub const SYS_FCNTL: usize = 55;
+pub const SYS_MPX: usize = 56;
+pub const SYS_SETPGID: usize = 57;
+pub const SYS_ULIMIT: usize = 58;
+pub const SYS_OLDOLDUNAME: usize = 59;
+pub const SYS_UMASK: usize = 60;
+pub const SYS_CHROOT: usize = 61;
+pub const SYS_USTAT: usize = 62;
+pub const SYS_DUP2: usize = 63;
+pub const SYS_GETPPID: usize = 64;
+pub const SYS_GETPGRP: usize = 65;
+pub const SYS_SETSID: usize = 66;
+pub const SYS_SIGACTION: usize = 67;
+pub const SYS_SGETMASK: usize = 68;
+pub const SYS_SSETMASK: usize = 69;
+pub const SYS_SETREUID: usize = 70;
+pub const SYS_SETREGID: usize = 71;
+pub const SYS_SIGSUSPEND: usize = 72;
+pub const SYS_SIGPENDING: usize = 73;
+pub const SYS_SETHOSTNAME: usize = 74;
+pub const SYS_SETRLIMIT: usize = 75;
+pub const SYS_GETRLIMIT: usize = 76;
+pub const SYS_GETRUSAGE: usize = 77;
+pub const SYS_GETTIMEOFDAY: usize = 78;
+pub const SYS_SETTIMEOFDAY: usize = 79;
+pub const SYS_GETGROUPS: usize = 80;
+pub const SYS_SETGROUPS: usize = 81;
+pub const SYS_SELECT: usize = 82;
+pub const SYS_SYMLINK: usize = 83;
+pub const SYS_OLDLSTAT: usize = 84;
+pub const SYS_READLINK: usize = 85;
+pub const SYS_USELIB: usize = 86;
+pub const SYS_SWAPON: usize = 87;
+pub const SYS_REBOOT: usize = 88;
+pub const SYS_READDIR: usize = 89;
+pub const SYS_MMAP: usize = 90;
+pub const SYS_MUNMAP: usize = 91;
+pub const SYS_TRUNCATE: usize = 92;
+pub const SYS_FTRUNCATE: usize = 93;
+pub const SYS_FCHMOD: usize = 94;
+pub const SYS_FCHOWN: usize = 95;
+pub const SYS_GETPRIORITY: usize = 96;
+pub const SYS_SETPRIORITY: usize = 97;
+pub const SYS_PROFIL: usize = 98;
+pub const SYS_STATFS: usize = 99;
+pub const SYS_FSTATFS: usize = 100;
+pub const SYS_IOPERM: usize = 101;
+pub const SYS_SOCKETCALL: usize = 102;
+pub const SYS_SYSLOG: usize = 103;
+pub const SYS_SETITIMER: usize = 104;
+pub const SYS_GETITIMER: usize = 105;
+pub const SYS_STAT: usize = 106;
+pub const SYS_LSTAT: usize = 107;
+pub const SYS_FSTAT: usize = 108;
+pub const SYS_OLDUNAME: usize = 109;
+pub const SYS_IOPL: usize = 110;
+pub const SYS_VHANGUP: usize = 111;
+pub const SYS_IDLE: usize = 112;
+pub const SYS_VM86OLD: usize = 113;
+pub const SYS_WAIT4: usize = 114;
+pub const SYS_SWAPOFF: usize = 115;
+pub const SYS_SYSINFO: usize = 116;
+pub const SYS_IPC: usize = 117;
+pub const SYS_FSYNC: usize = 118;
+pub const SYS_SIGRETURN: usize = 119;
+pub const SYS_CLONE: usize = 120;
+pub const SYS_SETDOMAINNAME: usize = 121;
+pub const SYS_NEWUNAME: usize = 122;
+pub const SYS_MODIFY_LDT: usize = 123;
+pub const SYS_ADJTIMEX: usize = 124;
+pub const SYS_MPROTECT: usize = 125;
+pub const SYS_SIGPROCMASK: usize = 126;
+pub const SYS_CREATE_MODULE: usize = 127;
+pub const SYS_INIT_MODULE: usize = 128;
+pub const SYS_DELETE_MODULE: usize = 129;
+pub const SYS_GET_KERNEL_SYMS: usize = 130;
+pub const SYS_QUOTACTL: usize = 131;
+pub const SYS_GETPGID: usize = 132;
+pub const SYS_FCHDIR: usize = 133;
+pub const SYS_BDFLUSH: usize = 134;
+pub const SYS_SYSFS: usize = 135;
+pub const SYS_PERSONALITY: usize = 136;
+pub const SYS_AFS_SYSCALL: usize = 137;
+pub const SYS_SETFSUID: usize = 138;
+pub const SYS_SETFSGID: usize = 139;
+pub const SYS_LLSEEK: usize = 140;
+pub const SYS_GETDENTS: usize = 141;
+pub const SYS__NEWSELECT: usize = 142;
+pub const SYS_FLOCK: usize = 143;
+pub const SYS_MSYNC: usize = 144;
+pub const SYS_READV: usize = 145;
+pub const SYS_WRITEV: usize = 146;
+pub const SYS_GETSID: usize = 147;
+pub const SYS_FDATASYNC: usize = 148;
+pub const SYS__SYSCTL: usize = 149;
+pub const SYS_MLOCK: usize = 150;
+pub const SYS_MUNLOCK: usize = 151;
+pub const SYS_MLOCKALL: usize = 152;
+pub const SYS_MUNLOCKALL: usize = 153;
+pub const SYS_SCHED_SETPARAM: usize = 154;
+pub const SYS_SCHED_GETPARAM: usize = 155;
+pub const SYS_SCHED_SETSCHEDULER: usize = 156;
+pub const SYS_SCHED_GETSCHEDULER: usize = 157;
+pub const SYS_SCHED_YIELD: usize = 158;
+pub const SYS_SCHED_GET_PRIORITY_MAX: usize = 159;
+pub const SYS_SCHED_GET_PRIORITY_MIN: usize = 160;
+pub const SYS_SCHED_RR_GET_INTERVAL: usize = 161;
+pub const SYS_NANOSLEEP: usize = 162;
+pub const SYS_MREMAP: usize = 163;
+pub const SYS_SETRESUID: usize = 164;
+pub const SYS_GETRESUID: usize = 165;
+pub const SYS_VM86: usize = 166;
+pub const SYS_QUERY_MODULE: usize = 167;
+pub const SYS_POLL: usize = 168;
+pub const SYS_NFSSERVCTL: usize = 169;
+pub const SYS_SETRESGID: usize = 170;
+pub const SYS_GETRESGID: usize = 171;
+pub const SYS_PRCTL: usize = 172;
+pub const SYS_RT_SIGRETURN: usize = 173;
+pub const SYS_RT_SIGACTION: usize = 174;
+pub const SYS_RT_SIGPROCMASK: usize = 175;
+pub const SYS_RT_SIGPENDING: usize = 176;
+pub const SYS_RT_SIGTIMEDWAIT: usize = 177;
+pub const SYS_RT_SIGQUEUEINFO: usize = 178;
+pub const SYS_RT_SIGSUSPEND: usize = 179;
+pub const SYS_PREAD64: usize = 180;
+pub const SYS_PWRITE64: usize = 181;
+pub const SYS_CHOWN: usize = 182;
+pub const SYS_GETCWD: usize = 183;
+pub const SYS_CAPGET: usize = 184;
+pub const SYS_CAPSET: usize = 185;
+pub const SYS_SIGALTSTACK: usize = 186;
+pub const SYS_SENDFILE: usize = 187;
+pub const SYS_GETPMSG: usize = 188;
+pub const SYS_PUTPMSG: usize = 189;
+pub const SYS_VFORK: usize = 190;
+pub const SYS_UGETRLIMIT: usize = 191;
+pub const SYS_MMAP2: usize = 192;
+pub const SYS_TRUNCATE64: usize = 193;
+pub const SYS_FTRUNCATE64: usize = 194;
+pub const SYS_STAT64: usize = 195;
+pub const SYS_LSTAT64: usize = 196;
+pub const SYS_FSTAT64: usize = 197;
+pub const SYS_LCHOWN32: usize = 198;
+pub const SYS_GETUID32: usize = 199;
+pub const SYS_GETGID32: usize = 200;
+pub const SYS_GETEUID32: usize = 201;
+pub const SYS_GETEGID32: usize = 202;
+pub const SYS_SETREUID32: usize = 203;
+pub const SYS_SETREGID32: usize = 204;
+pub const SYS_GETGROUPS32: usize = 205;
+pub const SYS_SETGROUPS32: usize = 206;
+pub const SYS_FCHOWN32: usize = 207;
+pub const SYS_SETRESUID32: usize = 208;
+pub const SYS_GETRESUID32: usize = 209;
+pub const SYS_SETRESGID32: usize = 210;
+pub const SYS_GETRESGID32: usize = 211;
+pub const SYS_CHOWN32: usize = 212;
+pub const SYS_SETUID32: usize = 213;
+pub const SYS_SETGID32: usize = 214;
+pub const SYS_SETFSUID32: usize = 215;
+pub const SYS_SETFSGID32: usize = 216;
+pub const SYS_PIVOT_ROOT: usize = 217;
+pub const SYS_MINCORE: usize = 218;
+pub const SYS_MADVISE: usize = 219;
+pub const SYS_GETDENTS64: usize = 220;
+pub const SYS_FCNTL64: usize = 221;
+pub const SYS_GETTID: usize = 224;
+pub const SYS_READAHEAD: usize = 225;
+pub const SYS_SETXATTR: usize = 226;
+pub const SYS_LSETXATTR: usize = 227;
+pub const SYS_FSETXATTR: usize = 228;
+pub const SYS_GETXATTR: usize = 229;
+pub const SYS_LGETXATTR: usize = 230;
+pub const SYS_FGETXATTR: usize = 231;
+pub const SYS_LISTXATTR: usize = 232;
+pub const SYS_LLISTXATTR: usize = 233;
+pub const SYS_FLISTXATTR: usize = 234;
+pub const SYS_REMOVEXATTR: usize = 235;
+pub const SYS_LREMOVEXATTR: usize = 236;
+pub const SYS_FREMOVEXATTR: usize = 237;
+pub const SYS_TKILL: usize = 238;
+pub const SYS_SENDFILE64: usize = 239;
+pub const SYS_FUTEX: usize = 240;
+pub const SYS_SCHED_SETAFFINITY: usize = 241;
+pub const SYS_SCHED_GETAFFINITY: usize = 242;
+pub const SYS_SET_THREAD_AREA: usize = 243;
+pub const SYS_GET_THREAD_AREA: usize = 244;
+pub const SYS_IO_SETUP: usize = 245;
+pub const SYS_IO_DESTROY: usize = 246;
+pub const SYS_IO_GETEVENTS: usize = 247;
+pub const SYS_IO_SUBMIT: usize = 248;
+pub const SYS_IO_CANCEL: usize = 249;
+pub const SYS_FADVISE64: usize = 250;
+pub const SYS_EXIT_GROUP: usize = 252;
+pub const SYS_LOOKUP_DCOOKIE: usize = 253;
+pub const SYS_EPOLL_CREATE: usize = 254;
+pub const SYS_EPOLL_CTL: usize = 255;
+pub const SYS_EPOLL_WAIT: usize = 256;
+pub const SYS_REMAP_FILE_PAGES: usize = 257;
+pub const SYS_SET_TID_ADDRESS: usize = 258;
+pub const SYS_TIMER_CREATE: usize = 259;
+pub const SYS_TIMER_SETTIME: usize = 260;
+pub const SYS_TIMER_GETTIME: usize = 261;
+pub const SYS_TIMER_GETOVERRUN: usize = 262;
+pub const SYS_TIMER_DELETE: usize = 263;
+pub const SYS_CLOCK_SETTIME: usize = 264;
+pub const SYS_CLOCK_GETTIME: usize = 265;
+pub const SYS_CLOCK_GETRES: usize = 266;
+pub const SYS_CLOCK_NANOSLEEP: usize = 267;
+pub const SYS_STATFS64: usize = 268;
+pub const SYS_FSTATFS64: usize = 269;
+pub const SYS_TGKILL: usize = 270;
+pub const SYS_UTIMES: usize = 271;
+pub const SYS_FADVISE64_64: usize = 272;
+pub const SYS_VSERVER: usize = 273;
+pub const SYS_MBIND: usize = 274;
+pub const SYS_GET_MEMPOLICY: usize = 275;
+pub const SYS_SET_MEMPOLICY: usize = 276;
+pub const SYS_MQ_OPEN: usize = 277;
+pub const SYS_MQ_UNLINK: usize = 278;
+pub const SYS_MQ_TIMEDSEND: usize = 279;
+pub const SYS_MQ_TIMEDRECEIVE: usize = 280;
+pub const SYS_MQ_NOTIFY: usize = 281;
+pub const SYS_MQ_GETSETATTR: usize = 282;
+pub const SYS_KEXEC_LOAD: usize = 283;
+pub const SYS_WAITID: usize = 284;
+pub const SYS_ADD_KEY: usize = 286;
+pub const SYS_REQUEST_KEY: usize = 287;
+pub const SYS_KEYCTL: usize = 288;
+pub const SYS_IOPRIO_SET: usize = 289;
+pub const SYS_IOPRIO_GET: usize = 290;
+pub const SYS_INOTIFY_INIT: usize = 291;
+pub const SYS_INOTIFY_ADD_WATCH: usize = 292;
+pub const SYS_INOTIFY_RM_WATCH: usize = 293;
+pub const SYS_MIGRATE_PAGES: usize = 294;
+pub const SYS_OPENAT: usize = 295;
+pub const SYS_MKDIRAT: usize = 296;
+pub const SYS_MKNODAT: usize = 297;
+pub const SYS_FCHOWNAT: usize = 298;
+pub const SYS_FUTIMESAT: usize = 299;
+pub const SYS_FSTATAT64: usize = 300;
+pub const SYS_UNLINKAT: usize = 301;
+pub const SYS_RENAMEAT: usize = 302;
+pub const SYS_LINKAT: usize = 303;
+pub const SYS_SYMLINKAT: usize = 304;
+pub const SYS_READLINKAT: usize = 305;
+pub const SYS_FCHMODAT: usize = 306;
+pub const SYS_FACCESSAT: usize = 307;
+pub const SYS_PSELECT6: usize = 308;
+pub const SYS_PPOLL: usize = 309;
+pub const SYS_UNSHARE: usize = 310;
+pub const SYS_SET_ROBUST_LIST: usize = 311;
+pub const SYS_GET_ROBUST_LIST: usize = 312;
+pub const SYS_SPLICE: usize = 313;
+pub const SYS_SYNC_FILE_RANGE: usize = 314;
+pub const SYS_TEE: usize = 315;
+pub const SYS_VMSPLICE: usize = 316;
+pub const SYS_MOVE_PAGES: usize = 317;
+pub const SYS_GETCPU: usize = 318;
+pub const SYS_EPOLL_PWAIT: usize = 319;
+pub const SYS_UTIMENSAT: usize = 320;
+pub const SYS_SIGNALFD: usize = 321;
+pub const SYS_TIMERFD_CREATE: usize = 322;
+pub const SYS_EVENTFD: usize = 323;
+pub const SYS_FALLOCATE: usize = 324;
+pub const SYS_TIMERFD_SETTIME: usize = 325;
+pub const SYS_TIMERFD_GETTIME: usize = 326;
+pub const SYS_SIGNALFD4: usize = 327;
+pub const SYS_EVENTFD2: usize = 328;
+pub const SYS_EPOLL_CREATE1: usize = 329;
+pub const SYS_DUP3: usize = 330;
+pub const SYS_PIPE2: usize = 331;
+pub const SYS_INOTIFY_INIT1: usize = 332;
+pub const SYS_PREADV: usize = 333;
+pub const SYS_PWRITEV: usize = 334;
+pub const SYS_RT_TGSIGQUEUEINFO: usize = 335;
+pub const SYS_PERF_EVENT_OPEN: usize = 336;
+pub const SYS_RECVMMSG: usize = 337;
+pub const SYS_FANOTIFY_INIT: usize = 338;
+pub const SYS_FANOTIFY_MARK: usize = 339;
+pub const SYS_PRLIMIT64: usize = 340;
+pub const SYS_NAME_TO_HANDLE_AT: usize = 341;
+pub const SYS_OPEN_BY_HANDLE_AT: usize = 342;
+pub const SYS_CLOCK_ADJTIME: usize = 343;
+pub const SYS_SYNCFS: usize = 344;
+pub const SYS_SENDMMSG: usize = 345;
+pub const SYS_SETNS: usize = 346;
+pub const SYS_PROCESS_VM_READV: usize = 347;
+pub const SYS_PROCESS_VM_WRITEV: usize = 348;
+pub const SYS_KCMP: usize = 349;
+pub const SYS_FINIT_MODULE: usize = 350;
+pub const SYS_SCHED_SETATTR: usize = 351;
+pub const SYS_SCHED_GETATTR: usize = 352;
+pub const SYS_RENAMEAT2: usize = 353;
+pub const SYS_SECCOMP: usize = 354;
+pub const SYS_GETRANDOM: usize = 355;
+pub const SYS_MEMFD_CREATE: usize = 356;
+pub const SYS_BPF: usize = 357;
+pub const SYS_EXECVEAT: usize = 358;
+pub const SYS_SOCKET: usize = 359;
+pub const SYS_SOCKETPAIR: usize = 360;
+pub const SYS_BIND: usize = 361;
+pub const SYS_CONNECT: usize = 362;
+pub const SYS_LISTEN: usize = 363;
+pub const SYS_ACCEPT4: usize = 364;
+pub const SYS_GETSOCKOPT: usize = 365;
+pub const SYS_SETSOCKOPT: usize = 366;
+pub const SYS_GETSOCKNAME: usize = 367;
+pub const SYS_GETPEERNAME: usize = 368;
+pub const SYS_SENDTO: usize = 369;
+pub const SYS_SENDMSG: usize = 370;
+pub const SYS_RECVFROM: usize = 371;
+pub const SYS_RECVMSG: usize = 372;
+pub const SYS_SHUTDOWN: usize = 373;
+pub const SYS_USERFAULTFD: usize = 374;
+pub const SYS_MEMBARRIER: usize = 375;
+pub const SYS_MLOCK2: usize = 376;
+pub const SYS_COPY_FILE_RANGE: usize = 377;
+pub const SYS_PREADV2: usize = 378;
+pub const SYS_PWRITEV2: usize = 379;
+pub const SYS_PKEY_MPROTECT: usize = 380;
+pub const SYS_PKEY_ALLOC: usize = 381;
+pub const SYS_PKEY_FREE: usize = 382;
+pub const SYS_STATX: usize = 383;
+pub const SYS_ARCH_PRCTL: usize = 384;
+pub const SYS_IO_PGETEVENTS: usize = 385;
+pub const SYS_RSEQ: usize = 386;
+pub const SYS_SEMGET: usize = 393;
+pub const SYS_SEMCTL: usize = 394;
+pub const SYS_SHMGET: usize = 395;
+pub const SYS_SHMCTL: usize = 396;
+pub const SYS_SHMAT: usize = 397;
+pub const SYS_SHMDT: usize = 398;
+pub const SYS_MSGGET: usize = 399;
+pub const SYS_MSGSND: usize = 400;
+pub const SYS_MSGRCV: usize = 401;
+pub const SYS_MSGCTL: usize = 402;
+pub const SYS_CLOCK_GETTIME64: usize = 403;
+pub const SYS_CLOCK_SETTIME64: usize = 404;
+pub const SYS_CLOCK_ADJTIME64: usize = 405;
+pub const SYS_CLOCK_GETRES_TIME64: usize = 406;
+pub const SYS_CLOCK_NANOSLEEP_TIME64: usize = 407;
+pub const SYS_TIMER_GETTIME64: usize = 408;
+pub const SYS_TIMER_SETTIME64: usize = 409;
+pub const SYS_TIMERFD_GETTIME64: usize = 410;
+pub const SYS_TIMERFD_SETTIME64: usize = 411;
+pub const SYS_UTIMENSAT_TIME64: usize = 412;
+pub const SYS_PSELECT6_TIME64: usize = 413;
+pub const SYS_PPOLL_TIME64: usize = 414;
+pub const SYS_IO_PGETEVENTS_TIME64: usize = 416;
+pub const SYS_RECVMMSG_TIME64: usize = 417;
+pub const SYS_MQ_TIMEDSEND_TIME64: usize = 418;
+pub const SYS_MQ_TIMEDRECEIVE_TIME64: usize = 419;
+pub const SYS_SEMTIMEDOP_TIME64: usize = 420;
+pub const SYS_RT_SIGTIMEDWAIT_TIME64: usize = 421;
+pub const SYS_FUTEX_TIME64: usize = 422;
+pub const SYS_SCHED_RR_GET_INTERVAL_TIME64: usize = 423;
+pub const SYS_PIDFD_SEND_SIGNAL: usize = 424;
+pub const SYS_IO_URING_SETUP: usize = 425;
+pub const SYS_IO_URING_ENTER: usize = 426;
+pub const SYS_IO_URING_REGISTER: usize = 427;
+pub const SYS_OPEN_TREE: usize = 428;
+pub const SYS_MOVE_MOUNT: usize = 429;
+pub const SYS_FSOPEN: usize = 430;
+pub const SYS_FSCONFIG: usize = 431;
+pub const SYS_FSMOUNT: usize = 432;
+pub const SYS_FSPICK: usize = 433;
+pub const SYS_PIDFD_OPEN: usize = 434;
+pub const SYS_CLONE3: usize = 435;
+pub const SYS_CLOSE_RANGE: usize = 436;
+pub const SYS_OPENAT2: usize = 437;
+pub const SYS_PIDFD_GETFD: usize = 438;
+pub const SYS_FACCESSAT2: usize = 439;
+pub const SYS_PROCESS_MADVISE: usize = 440;
+pub const SYS_EPOLL_PWAIT2: usize = 441;
+pub const SYS_MOUNT_SETATTR: usize = 442;

+ 13 - 0
doc/mem_layout_riscv64.txt

@@ -0,0 +1,13 @@
+Physical memory:
+
+0x0000 0000 - 0x7fff ffff  2GB MMIO
+0x8000 0000 - 0x801f ffff  2MB SBI
+0x8020 0000 - 0x803f ffff  2MB Kernel image
+0x8040 0000 - ?            ? B Free Memory after kernel image
+
+Kernel virtual address space:
+
+0xffff ff00 0000 0000 - 0xffff ff7f ffff ffff  512GB Physical Memory
+0xffff ff80 4000 0000 - ?                      ?   B Kernel Page Array
+0xffff ffff 4000 0000 - ?                      ?   B Kernel BSS
+0xffff ffff 8020 0000 - 0xffff ffff 803f ffff    2MB Kernel image

+ 38 - 23
macros/src/lib.rs

@@ -2,20 +2,14 @@ extern crate proc_macro;
 
 use proc_macro2::{Span, TokenStream};
 use quote::quote;
-use syn::{parse2, FnArg, Ident, ItemFn, LitInt, LitStr};
+use syn::{parse2, FnArg, Ident, ItemFn, 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 syscall_no = parse2::<Ident>(attrs).expect("Invalid syscall number");
     let item = parse2::<ItemFn>(item).unwrap();
 
     let attrs = item.attrs;
@@ -57,7 +51,7 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
 
     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),
+        &format!("_SYSCALL_ENTRY_{}", syscall_name.to_string().to_uppercase()),
         Span::call_site(),
     );
 
@@ -70,6 +64,29 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
     let syscall_fn_section =
         LitStr::new(&format!(".syscall_fns.{}", syscall_name), Span::call_site());
 
+    let trace_format_string = {
+        let arg_count = item.sig.inputs.len();
+        let brackets = (0..arg_count)
+            .map(|_| String::from("{:x?}"))
+            .collect::<Vec<_>>()
+            .join(", ");
+
+        LitStr::new(&brackets, Span::call_site())
+    };
+
+    let trace_format_args = {
+        let args = item.sig.inputs.iter();
+        let args = args.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 }
+            }
+        });
+
+        quote! { #(#args,)* }
+    };
+
     quote! {
         #[used]
         #[doc(hidden)]
@@ -91,23 +108,21 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
 
             #(#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),*),
-            // );
+            eonix_log::println_trace!(
+                "trace_syscall",
+                "tid{}: {}({}) => {{",
+                thd.tid,
+                #syscall_name_str,
+                format_args!(#trace_format_string, #trace_format_args),
+            );
 
             let retval = #real_fn(thd, #(#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),*),
-            // );
+            eonix_log::println_trace!(
+                "trace_syscall",
+                "}} => {:x?}",
+                retval,
+            );
 
             retval
         }

+ 0 - 1
pretty-print.py

@@ -185,5 +185,4 @@ gdb.execute('skip -rfu ^alloc::([a-zA-Z0-9_]+::)*[a-zA-Z0-9_<>]+')
 gdb.execute('skip -rfu ^std::([a-zA-Z0-9_]+::)*[a-zA-Z0-9_<>]+')
 gdb.execute('skip -rfu "^gbos_rust_part::sync::lock::Lock<[a-zA-Z0-9_<>: ,]+, [a-zA-Z0-9_<>: ,]+>::new<[a-zA-Z0-9_<>: ,]+, [a-zA-Z0-9_<>: ,]+>"')
 gdb.execute('skip -rfu "^gbos_rust_part::sync::locked::Locked<[a-zA-Z0-9_<>: ,]+, [a-zA-Z0-9_<>: ,]+>::new<[a-zA-Z0-9_<>: ,]+, [a-zA-Z0-9_<>: ,]+>"')
-# gdb.execute('source ' + environ['HOME'] + '/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/etc/gdb_load_rust_pretty_printers.py')
 gdb.pretty_printers.append(build_pretty_printer)

+ 26 - 12
script/build-img.sh

@@ -3,27 +3,41 @@
 OS=`uname -s`
 SUDO=sudo
 
-dd if=/dev/zero of=build/fs.img bs=`expr 1024 \* 1024` count=512
-mkfs.fat -n SYSTEM build/fs.img
+if [ "$OUTPUT" = "" ]; then
+    OUTPUT="build/fs-$ARCH.img"
+fi
+
+if [ "$ARCH" = "" ]; then
+    echo "ARCH is not set, exiting..." >&2
+    exit 1
+fi
+
+dd if=/dev/zero of="$OUTPUT" bs=`expr 1024 \* 1024` count=1020
+mkfs.fat -n SYSTEM "$OUTPUT"
 
 if [ "$OS" = "Darwin" ]; then
     SUDO=''
     hdiutil detach build/mnt > /dev/null 2>&1 || true
-    hdiutil attach build/fs.img -mountpoint build/mnt
+    hdiutil attach "$OUTPUT" -mountpoint build/mnt
 else
     mkdir -p build/mnt
-    $SUDO losetup -P /dev/loop2 build/fs.img
+    $SUDO losetup -P /dev/loop2 "$OUTPUT"
     $SUDO mount /dev/loop2 build/mnt
 fi
 
-$SUDO cp ./user-programs/init.out build/mnt/init
-$SUDO cp ./user-programs/int.out build/mnt/int
-$SUDO cp ./user-programs/dynamic_test build/mnt/dynamic_test
-$SUDO cp ./user-programs/busybox build/mnt/busybox
-$SUDO cp ./user-programs/busybox-minimal build/mnt/busybox_
-$SUDO cp ./user-programs/ld-musl-i386.so.1 build/mnt/ld-musl-i386.so.1
-$SUDO cp ./user-programs/pthread_test build/mnt/pthread_test
-$SUDO cp ./init_script.sh build/mnt/initsh
+if [ "$ARCH" = "x86_64" ]; then
+    $SUDO cp ./user-programs/init.out build/mnt/init
+    $SUDO cp ./user-programs/int.out build/mnt/int
+    $SUDO cp ./user-programs/dynamic_test build/mnt/dynamic_test
+    $SUDO cp ./user-programs/busybox build/mnt/busybox
+    $SUDO cp ./user-programs/busybox-minimal build/mnt/busybox_
+    $SUDO cp ./user-programs/ld-musl-i386.so.1 build/mnt/ld-musl-i386.so.1
+    $SUDO cp ./user-programs/pthread_test build/mnt/pthread_test
+    $SUDO cp ./user-programs/init_script_x86_64.sh build/mnt/initsh
+elif [ "$ARCH" = "riscv64" ]; then
+    $SUDO cp ./user-programs/busybox.static build/mnt/busybox
+    $SUDO cp ./user-programs/init_script_riscv64.sh build/mnt/initsh
+fi
 
 # Add your custom files here
 

+ 5 - 17
src/driver.rs

@@ -1,22 +1,10 @@
 pub mod ahci;
 pub mod e1000e;
+#[cfg(target_arch = "x86_64")]
 pub mod serial;
 
-// TODO!!!: Put it somewhere else.
-pub struct Port8 {
-    no: u16,
-}
+#[cfg(target_arch = "riscv64")]
+pub mod virtio;
 
-impl Port8 {
-    pub const fn new(no: u16) -> Self {
-        Self { no }
-    }
-
-    pub fn read(&self) -> u8 {
-        arch::inb(self.no)
-    }
-
-    pub fn write(&self, data: u8) {
-        arch::outb(self.no, data)
-    }
-}
+#[cfg(target_arch = "riscv64")]
+pub mod sbi_console;

+ 2 - 1
src/driver/ahci/control.rs

@@ -1,6 +1,7 @@
 use super::{BitsIterator, GHC_IE};
-use crate::{kernel::mem::PhysAccess as _, sync::fence::memory_barrier};
+use crate::kernel::mem::PhysAccess as _;
 use core::ptr::NonNull;
+use eonix_hal::fence::memory_barrier;
 use eonix_mm::address::PAddr;
 
 /// An `AdapterControl` is an HBA device Global Host Control block

+ 1 - 1
src/driver/ahci/register.rs

@@ -1,9 +1,9 @@
 use crate::{
     kernel::{constants::EIO, mem::PhysAccess as _},
-    sync::fence::memory_barrier,
     KResult,
 };
 use core::ptr::NonNull;
+use eonix_hal::fence::memory_barrier;
 use eonix_mm::address::PAddr;
 
 pub struct Register<T: Copy> {

+ 40 - 0
src/driver/sbi_console.rs

@@ -0,0 +1,40 @@
+use crate::kernel::{block::make_device, CharDevice, CharDeviceType, Terminal, TerminalDevice};
+use alloc::sync::Arc;
+use eonix_log::ConsoleWrite;
+
+struct SbiConsole;
+
+impl ConsoleWrite for SbiConsole {
+    fn write(&self, s: &str) {
+        eonix_hal::bootstrap::early_console_write(s);
+    }
+}
+
+impl TerminalDevice for SbiConsole {
+    fn write(&self, data: &[u8]) {
+        for &ch in data {
+            eonix_hal::bootstrap::early_console_putchar(ch);
+        }
+    }
+
+    fn write_direct(&self, data: &[u8]) {
+        for &ch in data {
+            eonix_hal::bootstrap::early_console_putchar(ch);
+        }
+    }
+}
+
+pub fn init_console() {
+    eonix_log::set_console(Arc::new(SbiConsole));
+
+    let console = Arc::new(SbiConsole);
+    let terminal = Terminal::new(console.clone());
+    crate::kernel::console::set_console(terminal.clone()).expect("Failed to set console");
+
+    CharDevice::register(
+        make_device(4, 64),
+        Arc::from("sbi_console"),
+        CharDeviceType::Terminal(terminal),
+    )
+    .expect("Failed to register SBI console as a character device");
+}

+ 10 - 5
src/driver/serial.rs

@@ -1,4 +1,3 @@
-use super::Port8;
 use crate::{
     kernel::{
         block::make_device, console::set_console, constants::EIO, interrupt::register_irq_handler,
@@ -9,9 +8,13 @@ use crate::{
 use alloc::{collections::vec_deque::VecDeque, format, sync::Arc};
 use bitflags::bitflags;
 use core::pin::pin;
+use eonix_hal::arch_exported::io::Port8;
 use eonix_runtime::{run::FutureRun, scheduler::Scheduler};
 use eonix_sync::{SpinIrq as _, WaitList};
 
+#[cfg(not(target_arch = "x86_64"))]
+compile_error!("Serial driver is only supported on x86_64 architecture");
+
 bitflags! {
     struct LineStatus: u8 {
         const RX_READY = 0x01;
@@ -197,14 +200,16 @@ impl Serial {
 }
 
 impl TerminalDevice for Serial {
-    fn putchar(&self, ch: u8) {
+    fn write(&self, data: &[u8]) {
         let mut tx_buffer = self.tx_buffer.lock();
-        tx_buffer.push_back(ch);
+        tx_buffer.extend(data.iter().copied());
         self.wakeup_worker();
     }
 
-    fn putchar_direct(&self, ch: u8) {
-        self.tx_rx.write(ch);
+    fn write_direct(&self, data: &[u8]) {
+        for &ch in data {
+            self.tx_rx.write(ch);
+        }
     }
 }
 

+ 155 - 0
src/driver/virtio.rs

@@ -0,0 +1,155 @@
+mod virtio_blk;
+
+#[cfg(not(target_arch = "riscv64"))]
+compile_error!("VirtIO drivers are only supported on RISC-V architecture");
+
+use crate::kernel::{
+    block::{make_device, BlockDevice},
+    mem::{AsMemoryBlock, MemoryBlock, Page},
+};
+use alloc::sync::Arc;
+use core::num::NonZero;
+use eonix_hal::{arch_exported::fdt::FDT, mm::ArchPhysAccess};
+use eonix_log::{println_info, println_warn};
+use eonix_mm::{
+    address::{Addr, PAddr, PhysAccess},
+    paging::PFN,
+};
+use eonix_sync::Spin;
+use virtio_drivers::{
+    device::blk::VirtIOBlk,
+    transport::{mmio::MmioTransport, Transport},
+    Hal,
+};
+
+pub struct HAL;
+
+unsafe impl Hal for HAL {
+    fn dma_alloc(
+        pages: usize,
+        _direction: virtio_drivers::BufferDirection,
+    ) -> (virtio_drivers::PhysAddr, core::ptr::NonNull<u8>) {
+        let page = Page::alloc_at_least(pages);
+
+        let paddr = page.start().addr();
+        let ptr = page.as_memblk().as_byte_ptr();
+        page.into_raw();
+
+        (paddr, ptr)
+    }
+
+    unsafe fn dma_dealloc(
+        paddr: virtio_drivers::PhysAddr,
+        _vaddr: core::ptr::NonNull<u8>,
+        _pages: usize,
+    ) -> i32 {
+        let pfn = PFN::from(PAddr::from(paddr));
+
+        unsafe {
+            // SAFETY: The caller ensures that the pfn corresponds to a valid
+            //         page allocated by `dma_alloc`.
+            Page::from_raw(pfn);
+        }
+
+        0
+    }
+
+    unsafe fn mmio_phys_to_virt(
+        paddr: virtio_drivers::PhysAddr,
+        size: usize,
+    ) -> core::ptr::NonNull<u8> {
+        MemoryBlock::new(NonZero::new(paddr).expect("paddr must be non-zero"), size).as_byte_ptr()
+    }
+
+    unsafe fn share(
+        buffer: core::ptr::NonNull<[u8]>,
+        _direction: virtio_drivers::BufferDirection,
+    ) -> virtio_drivers::PhysAddr {
+        let paddr = unsafe {
+            // SAFETY: The caller ensures that the buffer is valid.
+            ArchPhysAccess::from_ptr(buffer.cast::<u8>())
+        };
+
+        paddr.addr()
+    }
+
+    unsafe fn unshare(
+        _paddr: virtio_drivers::PhysAddr,
+        _buffer: core::ptr::NonNull<[u8]>,
+        _direction: virtio_drivers::BufferDirection,
+    ) {
+    }
+}
+
+pub fn init_virtio_devices() {
+    let mut disk_id = 0;
+    for reg in FDT
+        .all_nodes()
+        .filter(|node| {
+            node.compatible()
+                .is_some_and(|compatible| compatible.all().any(|s| s == "virtio,mmio"))
+        })
+        .filter_map(|node| node.reg())
+        .flatten()
+    {
+        let base = PAddr::from(reg.starting_address as usize);
+        let size = reg.size.expect("Virtio device must have a size");
+
+        let base = unsafe {
+            // SAFETY: We get the base address from the FDT, which is guaranteed to be valid.
+            ArchPhysAccess::as_ptr(base)
+        };
+
+        match unsafe { MmioTransport::new(base, size) } {
+            Ok(transport) => match transport.device_type() {
+                virtio_drivers::transport::DeviceType::Block => {
+                    let block_device = VirtIOBlk::<HAL, _>::new(transport)
+                        .expect("Failed to initialize VirtIO Block device");
+
+                    let block_device = BlockDevice::register_disk(
+                        make_device(8, 16 * disk_id),
+                        2147483647,
+                        Arc::new(Spin::new(block_device)),
+                    )
+                    .expect("Failed to register VirtIO Block device");
+
+                    block_device
+                        .partprobe()
+                        .expect("Failed to probe partitions for VirtIO Block device");
+
+                    disk_id += 1;
+                }
+                virtio_drivers::transport::DeviceType::Network => {
+                    println_info!(
+                        "Initializing Virtio Network device at {:?} with size {:#x}",
+                        base,
+                        size
+                    );
+                }
+                virtio_drivers::transport::DeviceType::Console => {
+                    println_info!(
+                        "Initializing Virtio Console at {:?} with size {:#x}",
+                        base,
+                        size
+                    );
+                }
+                virtio_drivers::transport::DeviceType::EntropySource => {
+                    println_info!(
+                        "Initializing Virtio Entropy Source at {:?} with size {:#x}",
+                        base,
+                        size
+                    );
+                }
+                _ => {}
+            },
+            Err(err) => {
+                println_warn!(
+                    "Failed to initialize Virtio device at {:?} with size {:#x}: {}",
+                    base,
+                    size,
+                    err
+                );
+            }
+        }
+    }
+}

+ 34 - 0
src/driver/virtio/virtio_blk.rs

@@ -0,0 +1,34 @@
+use super::HAL;
+use crate::{
+    io::Chunks,
+    kernel::{
+        block::{BlockDeviceRequest, BlockRequestQueue},
+        constants::EIO,
+        mem::AsMemoryBlock,
+    },
+    prelude::KResult,
+};
+use eonix_sync::Spin;
+use virtio_drivers::{device::blk::VirtIOBlk, transport::mmio::MmioTransport};
+
+impl BlockRequestQueue for Spin<VirtIOBlk<HAL, MmioTransport<'_>>> {
+    fn max_request_pages(&self) -> u64 {
+        1024
+    }
+
+    fn submit(&self, req: BlockDeviceRequest) -> KResult<()> {
+        let mut dev = self.lock();
+        for ((start, len), buffer_page) in
+            Chunks::new(req.sector as usize, req.count as usize, 8).zip(req.buffer.iter())
+        {
+            let buffer = unsafe {
+                // SAFETY: Pages in `req.buffer` are guaranteed to be exclusively owned by us.
+                &mut buffer_page.as_memblk().as_bytes_mut()[..len as usize * 512]
+            };
+
+            dev.read_blocks(start, buffer).map_err(|_| EIO)?;
+        }
+
+        Ok(())
+    }
+}

+ 22 - 11
src/fs/tmpfs.rs

@@ -1,3 +1,4 @@
+use crate::io::Stream;
 use crate::kernel::constants::{EINVAL, EIO, EISDIR};
 use crate::{
     io::Buffer,
@@ -274,32 +275,42 @@ impl Inode for FileInode {
         }
     }
 
-    fn write(&self, buffer: &[u8], offset: WriteOffset) -> KResult<usize> {
+    fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult<usize> {
         // TODO: We don't need that strong guarantee, find some way to avoid locks
         let lock = Task::block_on(self.rwsem.write());
         let filedata = self.filedata.access_mut(lock.prove_mut());
 
+        let mut store_new_end = None;
         let offset = match offset {
             WriteOffset::Position(offset) => offset,
-            // SAFETY: `lock` has done the synchronization
             WriteOffset::End(end) => {
-                let size = self.size.load(Ordering::Relaxed) as usize;
-                *end = size + buffer.len();
+                store_new_end = Some(end);
 
-                size
+                // SAFETY: `lock` has done the synchronization
+                self.size.load(Ordering::Relaxed) as usize
             }
         };
 
-        if filedata.len() < offset + buffer.len() {
-            filedata.resize(offset + buffer.len(), 0);
+        let mut pos = offset;
+        loop {
+            if pos >= filedata.len() {
+                filedata.resize(pos + 4096, 0);
+            }
+
+            match stream.poll_data(&mut filedata[pos..])? {
+                Some(data) => pos += data.len(),
+                None => break,
+            }
         }
 
-        filedata[offset..offset + buffer.len()].copy_from_slice(&buffer);
+        filedata.resize(pos, 0);
+        if let Some(store_end) = store_new_end {
+            *store_end = pos;
+        }
 
         // SAFETY: `lock` has done the synchronization
-        self.size.store(filedata.len() as u64, Ordering::Relaxed);
-
-        Ok(buffer.len())
+        self.size.store(pos as u64, Ordering::Relaxed);
+        Ok(pos - offset)
     }
 
     fn truncate(&self, length: usize) -> KResult<()> {

+ 96 - 0
src/io.rs

@@ -48,6 +48,53 @@ pub trait Buffer {
     }
 }
 
+pub trait Stream {
+    fn poll_data<'a>(&mut self, buf: &'a mut [u8]) -> KResult<Option<&'a mut [u8]>>;
+    fn ignore(&mut self, len: usize) -> KResult<Option<usize>>;
+}
+
+pub trait IntoStream {
+    type Stream: Stream;
+
+    fn into_stream(self) -> Self::Stream;
+}
+
+pub trait StreamRead {
+    fn read_till_end(
+        &mut self,
+        buffer: &mut [u8],
+        func: impl Fn(&mut [u8]) -> KResult<()>,
+    ) -> KResult<usize>;
+
+    fn ignore_all(&mut self) -> KResult<usize>;
+}
+
+impl<T> StreamRead for T
+where
+    T: Stream + ?Sized,
+{
+    fn read_till_end(
+        &mut self,
+        buffer: &mut [u8],
+        func: impl Fn(&mut [u8]) -> KResult<()>,
+    ) -> KResult<usize> {
+        let mut total = 0;
+        while let Some(data) = self.poll_data(buffer)? {
+            func(data)?;
+            total += data.len();
+        }
+        Ok(total)
+    }
+
+    fn ignore_all(&mut self) -> KResult<usize> {
+        let mut total = 0;
+        while let Some(len) = self.ignore(usize::MAX)? {
+            total += len;
+        }
+        Ok(total)
+    }
+}
+
 pub trait BufferFill<T: Copy> {
     fn copy(&mut self, object: &T) -> KResult<FillResult>;
 }
@@ -221,3 +268,52 @@ impl Iterator for Chunks {
         Some((start, len))
     }
 }
+
+pub struct ByteStream<'a> {
+    data: &'a [u8],
+    cur: usize,
+}
+
+impl<'a> ByteStream<'a> {
+    pub fn new(data: &'a [u8]) -> Self {
+        Self { data, cur: 0 }
+    }
+}
+
+impl<'a> Stream for ByteStream<'a> {
+    fn poll_data<'b>(&mut self, buf: &'b mut [u8]) -> KResult<Option<&'b mut [u8]>> {
+        if self.cur >= self.data.len() {
+            return Ok(None);
+        }
+
+        let end = core::cmp::min(self.data.len(), self.cur + buf.len());
+
+        let data = &self.data[self.cur..end];
+        let buf = &mut buf[..data.len()];
+
+        buf.copy_from_slice(data);
+        self.cur += data.len();
+
+        Ok(Some(buf))
+    }
+
+    fn ignore(&mut self, len: usize) -> KResult<Option<usize>> {
+        if self.cur >= self.data.len() {
+            return Ok(None);
+        }
+
+        let remaining = self.data.len() - self.cur;
+        let ignored = core::cmp::min(remaining, len);
+        self.cur += ignored;
+
+        Ok(Some(ignored))
+    }
+}
+
+impl<'a> IntoStream for &'a [u8] {
+    type Stream = ByteStream<'a>;
+
+    fn into_stream(self) -> Self::Stream {
+        ByteStream::new(self)
+    }
+}

+ 20 - 19
src/kernel/chardev.rs

@@ -9,7 +9,10 @@ use super::{
         DevId,
     },
 };
-use crate::{io::Buffer, prelude::*};
+use crate::{
+    io::{Buffer, Stream, StreamRead},
+    prelude::*,
+};
 use alloc::{
     boxed::Box,
     collections::btree_map::{BTreeMap, Entry},
@@ -20,7 +23,7 @@ use eonix_sync::AsProof as _;
 
 pub trait VirtualCharDevice: Send + Sync {
     fn read(&self, buffer: &mut dyn Buffer) -> KResult<usize>;
-    fn write(&self, data: &[u8]) -> KResult<usize>;
+    fn write(&self, stream: &mut dyn Stream) -> KResult<usize>;
 }
 
 pub enum CharDeviceType {
@@ -44,15 +47,13 @@ impl CharDevice {
         }
     }
 
-    pub fn write(&self, data: &[u8]) -> KResult<usize> {
+    pub fn write(&self, stream: &mut dyn Stream) -> KResult<usize> {
         match &self.device {
-            CharDeviceType::Virtual(device) => device.write(data),
-            CharDeviceType::Terminal(terminal) => {
-                for &ch in data.iter() {
-                    terminal.show_char(ch);
-                }
-                Ok(data.len())
-            }
+            CharDeviceType::Virtual(device) => device.write(stream),
+            CharDeviceType::Terminal(terminal) => stream.read_till_end(&mut [0; 128], |data| {
+                terminal.write(data);
+                Ok(())
+            }),
         }
     }
 
@@ -99,8 +100,8 @@ impl VirtualCharDevice for NullDevice {
         Ok(0)
     }
 
-    fn write(&self, _data: &[u8]) -> KResult<usize> {
-        Ok(_data.len())
+    fn write(&self, stream: &mut dyn Stream) -> KResult<usize> {
+        stream.ignore_all()
     }
 }
 
@@ -112,8 +113,8 @@ impl VirtualCharDevice for ZeroDevice {
         Ok(buffer.wrote())
     }
 
-    fn write(&self, _data: &[u8]) -> KResult<usize> {
-        Ok(_data.len())
+    fn write(&self, stream: &mut dyn Stream) -> KResult<usize> {
+        stream.ignore_all()
     }
 }
 
@@ -124,12 +125,12 @@ impl VirtualCharDevice for ConsoleDevice {
         Task::block_on(console_terminal.read(buffer))
     }
 
-    fn write(&self, data: &[u8]) -> KResult<usize> {
+    fn write(&self, stream: &mut dyn Stream) -> KResult<usize> {
         let console_terminal = get_console().ok_or(EIO)?;
-        for &ch in data.iter() {
-            console_terminal.show_char(ch);
-        }
-        Ok(data.len())
+        stream.read_till_end(&mut [0; 128], |data| {
+            console_terminal.write(data);
+            Ok(())
+        })
     }
 }
 

+ 0 - 21
src/kernel/constants.rs

@@ -54,37 +54,16 @@ pub const S_IFMT: u32 = 0o170000;
 
 pub const RLIMIT_STACK: u32 = 0x3;
 
-pub const AT_FDCWD: i32 = -100;
-pub const AT_STATX_SYNC_AS_STAT: u32 = 0;
-pub const AT_SYMLINK_NOFOLLOW: u32 = 0x0100;
-// pub const AT_REMOVEDIR: u32 = 0x0200;
-// pub const AT_SYMLINK_FOLLOW: u32 = 0x0400;
-pub const AT_EMPTY_PATH: u32 = 0x1000;
-// pub const AT_STATX_DONT_SYNC: u32 = 0x2000;
-pub const AT_STATX_SYNC_TYPE: u32 = 0x6000;
-// pub const AT_STATX_SYNC_FORCE: u32 = 0x8000;
-
 pub const SEEK_SET: u32 = 0;
 pub const SEEK_CUR: u32 = 1;
 pub const SEEK_END: u32 = 2;
 
-// pub const O_RDONLY: u32 = 0;
-pub const O_WRONLY: u32 = 1;
-pub const O_RDWR: u32 = 2;
-pub const O_CREAT: u32 = 64;
-pub const O_EXCL: u32 = 128;
-pub const O_TRUNC: u32 = 512;
-pub const O_APPEND: u32 = 1024;
-pub const O_DIRECTORY: u32 = 65536;
-pub const O_CLOEXEC: u32 = 524288;
-
 pub const F_DUPFD: u32 = 0;
 pub const F_GETFD: u32 = 1;
 pub const F_SETFD: u32 = 2;
 // pub const F_GETFL: u32 = 3;
 // pub const F_SETFL: u32 = 4;
 pub const F_DUPFD_CLOEXEC: u32 = 1030;
-pub const FD_CLOEXEC: u32 = 1;
 
 pub const STATX_TYPE: u32 = 1;
 pub const STATX_MODE: u32 = 2;

+ 15 - 8
src/kernel/interrupt.rs

@@ -1,7 +1,7 @@
 use super::mem::handle_kernel_page_fault;
 use super::timer::timer_interrupt;
 use crate::kernel::constants::EINVAL;
-use crate::{driver::Port8, prelude::*};
+use crate::prelude::*;
 use alloc::sync::Arc;
 use eonix_hal::processor::CPU;
 use eonix_hal::traits::fault::Fault;
@@ -22,12 +22,17 @@ pub fn default_irq_handler(irqno: usize) {
         handler();
     }
 
-    const PIC1_COMMAND: Port8 = Port8::new(0x20);
-    const PIC2_COMMAND: Port8 = Port8::new(0xA0);
+    #[cfg(target_arch = "x86_64")]
+    {
+        use eonix_hal::arch_exported::io::Port8;
 
-    PIC1_COMMAND.write(0x20); // EOI
-    if irqno >= 8 {
-        PIC2_COMMAND.write(0x20); // EOI
+        const PIC1_COMMAND: Port8 = Port8::new(0x20);
+        const PIC2_COMMAND: Port8 = Port8::new(0xA0);
+
+        PIC1_COMMAND.write(0x20); // EOI
+        if irqno >= 8 {
+            PIC2_COMMAND.write(0x20); // EOI
+        }
     }
 }
 
@@ -37,9 +42,11 @@ pub fn default_fault_handler(fault_type: Fault, trap_ctx: &mut TrapContext) {
     }
 
     match fault_type {
-        Fault::PageFault(error_code) => {
+        Fault::PageFault {
+            error_code,
+            address: vaddr,
+        } => {
             let fault_pc = VAddr::from(trap_ctx.get_program_counter());
-            let vaddr = arch::get_page_fault_address();
 
             if let Some(new_pc) = handle_kernel_page_fault(fault_pc, vaddr, error_code) {
                 trap_ctx.set_program_counter(new_pc.addr());

+ 40 - 51
src/kernel/mem/mm_area.rs

@@ -81,27 +81,23 @@ impl MMArea {
         }
     }
 
-    /// # Return
-    /// Whether the whole handling process is done.
-    pub fn handle_cow(&self, pte: &mut impl PTE) -> bool {
-        let mut page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
-        let pfn = pte.get_pfn();
+    pub fn handle_cow(&self, pfn: &mut PFN, attr: &mut PageAttribute) {
+        assert!(attr.contains(PageAttribute::COPY_ON_WRITE));
 
-        page_attr.remove(PageAttribute::COPY_ON_WRITE);
-        page_attr.set(PageAttribute::WRITE, self.permission.write);
+        attr.remove(PageAttribute::COPY_ON_WRITE);
+        attr.set(PageAttribute::WRITE, self.permission.write);
 
-        let page = unsafe { Page::from_raw(pfn) };
+        let page = unsafe { Page::from_raw(*pfn) };
         if page.is_exclusive() {
             // SAFETY: This is actually safe. If we read `1` here and we have `MMList` lock
             // held, there couldn't be neither other processes sharing the page, nor other
             // threads making the page COW at the same time.
-            pte.set_attr(page_attr.into());
             core::mem::forget(page);
-            return true;
+            return;
         }
 
         let new_page;
-        if is_empty_page(pfn) {
+        if *pfn == EMPTY_PAGE.pfn() {
             new_page = Page::zeroed();
         } else {
             new_page = Page::alloc();
@@ -117,33 +113,30 @@ impl MMArea {
             };
         }
 
-        page_attr.remove(PageAttribute::ACCESSED);
-
-        pte.set(new_page.into_raw(), page_attr.into());
-
-        false
+        attr.remove(PageAttribute::ACCESSED);
+        *pfn = new_page.into_raw();
     }
 
     /// # Arguments
     /// * `offset`: The offset from the start of the mapping, aligned to 4KB boundary.
-    pub fn handle_mmap(&self, pte: &mut impl PTE, offset: usize) -> KResult<()> {
+    pub fn handle_mmap(
+        &self,
+        pfn: &mut PFN,
+        attr: &mut PageAttribute,
+        offset: usize,
+    ) -> KResult<()> {
         // TODO: Implement shared mapping
-        let mut page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
-        let pfn = pte.get_pfn();
-
-        match &self.mapping {
-            Mapping::File(mapping) if offset < mapping.length => {
-                let page = unsafe {
-                    // SAFETY: Since we are here, the `pfn` must refer to a valid buddy page.
-                    Page::with_raw(pfn, |page| page.clone())
-                };
-
-                let page_data = unsafe {
-                    // SAFETY: `page` is marked as mapped, so others trying to read or write to
-                    //         it will be blocked and enter the page fault handler, where they will
-                    //         be blocked by the mutex held by us.
-                    page.as_memblk().as_bytes_mut()
-                };
+        let Mapping::File(mapping) = &self.mapping else {
+            panic!("Anonymous mapping should not be PA_MMAP");
+        };
+
+        assert!(offset < mapping.length, "Offset out of range");
+        unsafe {
+            Page::with_raw(*pfn, |page| {
+                // SAFETY: `page` is marked as mapped, so others trying to read or write to
+                //         it will be blocked and enter the page fault handler, where they will
+                //         be blocked by the mutex held by us.
+                let page_data = page.as_memblk().as_bytes_mut();
 
                 let cnt_to_read = (mapping.length - offset).min(0x1000);
                 let cnt_read = mapping.file.read(
@@ -152,39 +145,35 @@ impl MMArea {
                 )?;
 
                 page_data[cnt_read..].fill(0);
-            }
-            Mapping::File(_) => panic!("Offset out of range"),
-            _ => panic!("Anonymous mapping should not be PA_MMAP"),
-        }
 
-        page_attr.insert(PageAttribute::PRESENT);
-        page_attr.remove(PageAttribute::MAPPED);
+                KResult::Ok(())
+            })?;
+        }
 
-        pte.set_attr(page_attr.into());
+        attr.insert(PageAttribute::PRESENT);
+        attr.remove(PageAttribute::MAPPED);
         Ok(())
     }
 
     pub fn handle(&self, pte: &mut impl PTE, offset: usize) -> KResult<()> {
-        let page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
+        let mut attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
+        let mut pfn = pte.get_pfn();
 
-        if page_attr.contains(PageAttribute::COPY_ON_WRITE) {
-            self.handle_cow(pte);
+        if attr.contains(PageAttribute::COPY_ON_WRITE) {
+            self.handle_cow(&mut pfn, &mut attr);
         }
 
-        if page_attr.contains(PageAttribute::MAPPED) {
-            self.handle_mmap(pte, offset)?;
+        if attr.contains(PageAttribute::MAPPED) {
+            self.handle_mmap(&mut pfn, &mut attr, offset)?;
         }
 
+        attr.set(PageAttribute::ACCESSED, true);
+        pte.set(pfn, attr.into());
+
         Ok(())
     }
 }
 
-/// check pfn with EMPTY_PAGE's pfn
-fn is_empty_page(pfn: PFN) -> bool {
-    let empty_pfn = EMPTY_PAGE.pfn();
-    pfn == empty_pfn
-}
-
 impl Eq for MMArea {}
 impl PartialEq for MMArea {
     fn eq(&self, other: &Self) -> bool {

+ 79 - 10
src/kernel/mem/mm_list.rs

@@ -10,7 +10,10 @@ use crate::{prelude::*, sync::ArcSwap};
 use alloc::collections::btree_set::BTreeSet;
 use core::fmt;
 use core::sync::atomic::{AtomicUsize, Ordering};
-use eonix_hal::mm::{ArchPagingMode, ArchPhysAccess, GLOBAL_PAGE_TABLE};
+use eonix_hal::mm::{
+    flush_tlb_all, get_root_page_table_pfn, set_root_page_table_pfn, ArchPagingMode,
+    ArchPhysAccess, GLOBAL_PAGE_TABLE,
+};
 use eonix_mm::address::{Addr as _, PAddr};
 use eonix_mm::page_table::PageAttribute;
 use eonix_mm::paging::PFN;
@@ -212,6 +215,19 @@ impl MMListInner<'_> {
             for pte in self.page_table.iter_user(mid) {
                 let mut page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
 
+                if !permission.read && !permission.write && !permission.execute {
+                    // If no permissions are set, we just remove the page.
+                    page_attr.remove(
+                        PageAttribute::PRESENT
+                            | PageAttribute::READ
+                            | PageAttribute::WRITE
+                            | PageAttribute::EXECUTE,
+                    );
+
+                    pte.set_attr(page_attr.into());
+                    continue;
+                }
+
                 page_attr.set(PageAttribute::READ, permission.read);
 
                 if !page_attr.contains(PageAttribute::COPY_ON_WRITE) {
@@ -260,6 +276,12 @@ impl MMListInner<'_> {
     }
 }
 
+impl Drop for MMListInner<'_> {
+    fn drop(&mut self) {
+        // TODO: Recycle all pages in the page table.
+    }
+}
+
 impl MMList {
     async fn flush_user_tlbs(&self) {
         match self.user_count.load(Ordering::Relaxed) {
@@ -267,12 +289,12 @@ impl MMList {
                 // If there are currently no users, we don't need to do anything.
             }
             1 => {
-                if PAddr::from(arch::get_root_page_table_pfn()).addr()
+                if PAddr::from(get_root_page_table_pfn()).addr()
                     == self.root_page_table.load(Ordering::Relaxed)
                 {
                     // If there is only one user and we are using the page table,
                     // flushing the TLB for the local cpu only is enough.
-                    arch::flush_tlb_all();
+                    flush_tlb_all();
                 } else {
                     // Send the TLB flush request to the core.
                     todo!();
@@ -342,11 +364,11 @@ impl MMList {
 
         let root_page_table = self.root_page_table.load(Ordering::Relaxed);
         assert_ne!(root_page_table, 0);
-        arch::set_root_page_table_pfn(PFN::from(PAddr::from(root_page_table)));
+        set_root_page_table_pfn(PFN::from(PAddr::from(root_page_table)));
     }
 
     pub fn deactivate(&self) {
-        arch::set_root_page_table_pfn(PFN::from(GLOBAL_PAGE_TABLE.addr()));
+        set_root_page_table_pfn(PFN::from(GLOBAL_PAGE_TABLE.addr()));
 
         let old_user_count = self.user_count.fetch_sub(1, Ordering::Release);
         assert_ne!(old_user_count, 0);
@@ -360,7 +382,7 @@ impl MMList {
 
         let root_page_table = self.root_page_table.load(Ordering::Relaxed);
         assert_ne!(root_page_table, 0);
-        arch::set_root_page_table_pfn(PFN::from(PAddr::from(root_page_table)));
+        set_root_page_table_pfn(PFN::from(PAddr::from(root_page_table)));
 
         let old_user_count = to.user_count.fetch_sub(1, Ordering::Release);
         assert_ne!(old_user_count, 0);
@@ -389,7 +411,7 @@ impl MMList {
         );
 
         let old_root_page_table = self.root_page_table.load(Ordering::Relaxed);
-        let current_root_page_table = arch::get_root_page_table_pfn();
+        let current_root_page_table = get_root_page_table_pfn();
         assert_eq!(
             PAddr::from(current_root_page_table).addr(),
             old_root_page_table,
@@ -401,7 +423,7 @@ impl MMList {
             None => GLOBAL_PAGE_TABLE.addr().addr(),
         };
 
-        arch::set_root_page_table_pfn(PFN::from(PAddr::from(new_root_page_table)));
+        set_root_page_table_pfn(PFN::from(PAddr::from(new_root_page_table)));
 
         self.root_page_table
             .store(new_root_page_table, Ordering::Relaxed);
@@ -438,6 +460,47 @@ impl MMList {
         Ok(())
     }
 
+    pub fn map_vdso(&self) -> KResult<()> {
+        unsafe extern "C" {
+            fn VDSO_PADDR();
+        }
+        static VDSO_PADDR_VALUE: &'static unsafe extern "C" fn() =
+            &(VDSO_PADDR as unsafe extern "C" fn());
+
+        let vdso_paddr = unsafe {
+            // SAFETY: To prevent the compiler from optimizing this into `la` instructions
+            //         and causing a linking error.
+            (VDSO_PADDR_VALUE as *const _ as *const usize).read_volatile()
+        };
+
+        let vdso_pfn = PFN::from(PAddr::from(vdso_paddr));
+
+        const VDSO_START: VAddr = VAddr::from(0x7f00_0000_0000);
+        const VDSO_SIZE: usize = 0x1000;
+
+        let inner = self.inner.borrow();
+        let inner = Task::block_on(inner.lock());
+
+        let mut pte_iter = inner
+            .page_table
+            .iter_user(VRange::from(VDSO_START).grow(VDSO_SIZE));
+
+        let pte = pte_iter.next().expect("There should be at least one PTE");
+        pte.set(
+            vdso_pfn,
+            (PageAttribute::PRESENT
+                | PageAttribute::READ
+                | PageAttribute::EXECUTE
+                | PageAttribute::USER
+                | PageAttribute::ACCESSED)
+                .into(),
+        );
+
+        assert!(pte_iter.next().is_none(), "There should be only one PTE");
+
+        Ok(())
+    }
+
     pub fn mmap_hint(
         &self,
         hint: VAddr,
@@ -663,7 +726,10 @@ where
     fn set_anonymous(&mut self, execute: bool) {
         // Writable flag is set during page fault handling while executable flag is
         // preserved across page faults, so we set executable flag now.
-        let mut attr = PageAttribute::PRESENT | PageAttribute::USER | PageAttribute::COPY_ON_WRITE;
+        let mut attr = PageAttribute::PRESENT
+            | PageAttribute::READ
+            | PageAttribute::USER
+            | PageAttribute::COPY_ON_WRITE;
         attr.set(PageAttribute::EXECUTE, execute);
 
         self.set(EMPTY_PAGE.clone().into_raw(), T::Attr::from(attr));
@@ -672,7 +738,10 @@ where
     fn set_mapped(&mut self, execute: bool) {
         // Writable flag is set during page fault handling while executable flag is
         // preserved across page faults, so we set executable flag now.
-        let mut attr = PageAttribute::MAPPED | PageAttribute::USER | PageAttribute::COPY_ON_WRITE;
+        let mut attr = PageAttribute::READ
+            | PageAttribute::USER
+            | PageAttribute::MAPPED
+            | PageAttribute::COPY_ON_WRITE;
         attr.set(PageAttribute::EXECUTE, execute);
 
         self.set(EMPTY_PAGE.clone().into_raw(), T::Attr::from(attr));

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác