Преглед изворни кода

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

Riscv64 support with no serial terminal support for now.
greatbridf пре 7 месеци
родитељ
комит
e5d5464c96
100 измењених фајлова са 4779 додато и 1045 уклоњено
  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));

Неке датотеке нису приказане због велике количине промена