Browse Source

Merge pull request #6 from greatbridf/remove-cpp

Refactor and optimize trap handling and PCIe driver
greatbridf 11 tháng trước cách đây
mục cha
commit
2b8042afc2
100 tập tin đã thay đổi với 3901 bổ sung1995 xóa
  1. 0 29
      .clang-format
  2. 0 2
      .gitignore
  3. 2 2
      .vscode/launch.json
  4. 0 1
      .vscode/settings.json
  5. 2 2
      .vscode/tasks.json
  6. 0 95
      CMakeLists.txt
  7. 123 227
      Cargo.lock
  8. 7 6
      Cargo.toml
  9. 66 40
      Makefile.src
  10. 19 14
      README.md
  11. 1 1
      arch/Cargo.toml
  12. 9 0
      arch/arch_macros/Cargo.toml
  13. 1 0
      arch/arch_macros/src/lib.rs
  14. 1 0
      arch/arch_macros/src/x86_64/mod.rs
  15. 32 0
      arch/arch_macros/src/x86_64/percpu.rs
  16. 0 47
      arch/percpu-macros/Cargo.lock
  17. 0 24
      arch/percpu-macros/src/arch.rs
  18. 0 1
      arch/src/lib.rs
  19. 32 0
      arch/src/x86_64/fpu.rs
  20. 0 555
      arch/src/x86_64/interrupt.rs
  21. 0 17
      arch/src/x86_64/io.rs
  22. 0 202
      arch/src/x86_64/mm.rs
  23. 2 23
      arch/src/x86_64/mod.rs
  24. 0 86
      arch/src/x86_64/percpu.rs
  25. 0 54
      arch/src/x86_64/user.rs
  26. 8 26
      build.rs
  27. 39 90
      configure
  28. 25 2
      crates/buddy_allocator/src/zone.rs
  29. 18 0
      crates/eonix_hal/Cargo.toml
  30. 49 0
      crates/eonix_hal/build.rs
  31. 12 0
      crates/eonix_hal/eonix_hal_macros/Cargo.toml
  32. 165 0
      crates/eonix_hal/eonix_hal_macros/src/lib.rs
  33. 8 0
      crates/eonix_hal/eonix_hal_traits/Cargo.toml
  34. 41 0
      crates/eonix_hal/eonix_hal_traits/src/context.rs
  35. 20 0
      crates/eonix_hal/eonix_hal_traits/src/fault.rs
  36. 6 0
      crates/eonix_hal/eonix_hal_traits/src/fpu.rs
  37. 9 0
      crates/eonix_hal/eonix_hal_traits/src/lib.rs
  38. 6 0
      crates/eonix_hal/eonix_hal_traits/src/mm.rs
  39. 7 0
      crates/eonix_hal/eonix_hal_traits/src/processor.rs
  40. 95 0
      crates/eonix_hal/eonix_hal_traits/src/trap.rs
  41. 14 0
      crates/eonix_hal/src/arch/mod.rs
  42. 516 0
      crates/eonix_hal/src/arch/x86_64/bootstrap.rs
  43. 374 0
      crates/eonix_hal/src/arch/x86_64/bootstrap/init.rs
  44. 46 77
      crates/eonix_hal/src/arch/x86_64/context.rs
  45. 132 60
      crates/eonix_hal/src/arch/x86_64/cpu.rs
  46. 28 23
      crates/eonix_hal/src/arch/x86_64/gdt.rs
  47. 196 0
      crates/eonix_hal/src/arch/x86_64/interrupt.rs
  48. 98 0
      crates/eonix_hal/src/arch/x86_64/link.x
  49. 11 0
      crates/eonix_hal/src/arch/x86_64/memory.x
  50. 375 0
      crates/eonix_hal/src/arch/x86_64/mm.rs
  51. 7 0
      crates/eonix_hal/src/arch/x86_64/mod.rs
  52. 390 0
      crates/eonix_hal/src/arch/x86_64/trap.rs
  53. 130 0
      crates/eonix_hal/src/arch/x86_64/trap/trap_context.rs
  54. 22 0
      crates/eonix_hal/src/bootstrap.rs
  55. 1 0
      crates/eonix_hal/src/context.rs
  56. 14 0
      crates/eonix_hal/src/lib.rs
  57. 114 0
      crates/eonix_hal/src/link.x.in
  58. 198 0
      crates/eonix_hal/src/mm.rs
  59. 1 0
      crates/eonix_hal/src/processor.rs
  60. 5 0
      crates/eonix_hal/src/trap.rs
  61. 1 1
      crates/eonix_mm/src/address.rs
  62. 2 0
      crates/eonix_mm/src/address/addr.rs
  63. 24 0
      crates/eonix_mm/src/address/paddr.rs
  64. 1 0
      crates/eonix_mm/src/lib.rs
  65. 54 22
      crates/eonix_mm/src/page_table/page_table.rs
  66. 1 5
      crates/eonix_mm/src/page_table/paging_mode.rs
  67. 4 13
      crates/eonix_mm/src/page_table/pte.rs
  68. 1 1
      crates/eonix_mm/src/page_table/pte_iterator.rs
  69. 2 2
      crates/eonix_mm/src/paging.rs
  70. 17 3
      crates/eonix_mm/src/paging/page.rs
  71. 28 1
      crates/eonix_mm/src/paging/page_alloc.rs
  72. 41 0
      crates/eonix_mm/src/paging/raw_page.rs
  73. 1 6
      crates/eonix_percpu/Cargo.toml
  74. 14 0
      crates/eonix_percpu/eonix_percpu_macros/Cargo.toml
  75. 42 13
      crates/eonix_percpu/eonix_percpu_macros/src/lib.rs
  76. 0 24
      crates/eonix_percpu/src/arch.rs
  77. 58 160
      crates/eonix_percpu/src/lib.rs
  78. 1 1
      crates/eonix_preempt/Cargo.toml
  79. 42 11
      crates/eonix_preempt/src/lib.rs
  80. 2 0
      crates/eonix_runtime/Cargo.toml
  81. 10 10
      crates/eonix_runtime/src/context.rs
  82. 1 1
      crates/eonix_runtime/src/ready_queue.rs
  83. 3 2
      crates/eonix_runtime/src/scheduler.rs
  84. 6 7
      crates/eonix_sync/Cargo.toml
  85. 8 0
      crates/eonix_sync/eonix_spin/Cargo.toml
  86. 1 1
      crates/eonix_sync/eonix_spin/src/guard.rs
  87. 11 4
      crates/eonix_sync/eonix_spin/src/lib.rs
  88. 10 0
      crates/eonix_sync/eonix_sync_base/Cargo.toml
  89. 0 0
      crates/eonix_sync/eonix_sync_base/src/guard.rs
  90. 0 0
      crates/eonix_sync/eonix_sync_base/src/lazy_lock.rs
  91. 13 0
      crates/eonix_sync/eonix_sync_base/src/lib.rs
  92. 0 0
      crates/eonix_sync/eonix_sync_base/src/locked.rs
  93. 0 0
      crates/eonix_sync/eonix_sync_base/src/locked/proof.rs
  94. 0 0
      crates/eonix_sync/eonix_sync_base/src/marker.rs
  95. 0 0
      crates/eonix_sync/eonix_sync_base/src/relax.rs
  96. 13 0
      crates/eonix_sync/eonix_sync_rt/Cargo.toml
  97. 11 0
      crates/eonix_sync/eonix_sync_rt/src/lib.rs
  98. 0 0
      crates/eonix_sync/eonix_sync_rt/src/mutex.rs
  99. 1 1
      crates/eonix_sync/eonix_sync_rt/src/mutex/guard.rs
  100. 0 0
      crates/eonix_sync/eonix_sync_rt/src/rwlock.rs

+ 0 - 29
.clang-format

@@ -1,29 +0,0 @@
----
-BasedOnStyle: Google
-AllowShortBlocksOnASingleLine: 'false'
-AllowShortCaseLabelsOnASingleLine: 'false'
-AllowShortFunctionsOnASingleLine: Inline
-AllowShortIfStatementsOnASingleLine: Never
-AllowShortLoopsOnASingleLine: 'false'
-BreakConstructorInitializers: BeforeComma
-ColumnLimit: '100'
-FixNamespaceComments: 'true'
-IncludeBlocks: Regroup
-IndentWidth: '4'
-NamespaceIndentation: Inner
-SpacesBeforeTrailingComments: '1'
-Language: Cpp
-Standard: Cpp11
-IncludeCategories:
-  - Regex: '^<types/'
-    Priority: '4'
-  - Regex: '^<(kernel|fs|net|driver)/'
-    Priority: '5'
-  - Regex: '^<.*\.h>'
-    Priority: '3'
-  - Regex: '^<.*>'
-    Priority: '2'
-  - Regex: '.*'
-    Priority: '1'
-
-...

+ 0 - 2
.gitignore

@@ -19,5 +19,3 @@ cross-compile.cmake
 .Trashes
 
 .gdbinit
-
-src/bindings.rs

+ 2 - 2
.vscode/launch.json

@@ -4,7 +4,7 @@
             "type": "cppdbg",
             "request": "launch",
             "name": "Launch Kernel",
-            "program": "${workspaceFolder}/build/kernel.out",
+            "program": "${workspaceFolder}/build/kernel.sym",
             "args": [],
             "stopAtEntry": false,
             "cwd": "${workspaceFolder}",
@@ -37,7 +37,7 @@
             "type": "cppdbg",
             "request": "launch",
             "name": "Attach Kernel",
-            "program": "${workspaceFolder}/build/kernel.out",
+            "program": "${workspaceFolder}/build/kernel.sym",
             "args": [],
             "stopAtEntry": false,
             "cwd": "${workspaceFolder}",

+ 0 - 1
.vscode/settings.json

@@ -1,4 +1,3 @@
 {
     "makefile.configureOnOpen": false,
-    "rust-analyzer.check.allTargets": false,
 }

+ 2 - 2
.vscode/tasks.json

@@ -10,7 +10,7 @@
             "isBackground": true,
             "problemMatcher": [
                 {
-                    "owner": "cpp",
+                    "owner": "rustc",
                     "fileLocation": [
                         "relative",
                         "${workspaceFolder}"
@@ -25,7 +25,7 @@
                     },
                     "background": {
                         "activeOnStart": true,
-                        "beginsPattern": "cmake --build",
+                        "beginsPattern": "cargo build",
                         "endsPattern": "qemu"
                     }
                 }

+ 0 - 95
CMakeLists.txt

@@ -1,95 +0,0 @@
-cmake_minimum_required(VERSION 3.15)
-project(kernel_main C CXX ASM)
-
-set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-
-set(CMAKE_CXX_LINK_EXECUTABLE
-    "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
-
-set(C_CXX_FLAGS "-nostdinc -nostdlib -mno-sse -mno-mmx -W -Wall -Wextra -Wno-stringop-overflow -Wno-builtin-declaration-mismatch -Wno-format -fverbose-asm -fno-exceptions -ffreestanding -fno-pic -mno-red-zone -mstack-protector-guard=global -mcmodel=kernel")
-set(CMAKE_C_FLAGS "${C_CXX_FLAGS} -Werror=implicit-int -Werror=implicit-function-declaration -Werror=strict-aliasing")
-set(CMAKE_CXX_FLAGS "${C_CXX_FLAGS} -fno-use-cxa-atexit -fno-rtti")
-set(CMAKE_CXX_LINK_FLAGS "-z noexecstack --gc-sections")
-SET(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
-set(CMAKE_CXX_STANDARD 20)
-
-if (CMAKE_BUILD_TYPE STREQUAL "Debug")
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG -O0 -g")
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG -O0 -g")
-    set(CARGO_BUILD_TYPE "debug")
-    set(CARGO_BUILD_FLAGS "")
-elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -g -DNDEBUG")
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -g -DNDEBUG")
-    set(CARGO_BUILD_TYPE "release")
-    set(CARGO_BUILD_FLAGS "--release")
-endif()
-
-if (NOT DEFINED FDISK_BIN)
-    set(FDISK_BIN fdisk)
-endif()
-
-add_subdirectory(gblibc)
-add_subdirectory(gblibstdc++)
-add_subdirectory(user-space-program)
-
-set(BOOTLOADER_SOURCES src/boot.s
-                       src/mbr.S
-                       )
-
-set(KERNEL_MAIN_SOURCES src/kinit.cpp
-                        src/kernel/async/lock.cc
-                        src/kernel/allocator.cc
-                        src/kernel/mem/slab.cc
-                        src/kernel/hw/acpi.cc
-                        src/kernel/hw/pci.cc
-                        src/types/libstdcpp.cpp
-                        include/defs.hpp
-                        include/kernel/async/lock.hpp
-                        include/kernel/mem/paging.hpp
-                        include/kernel/mem/slab.hpp
-                        include/kernel/mem/types.hpp
-                        include/kernel/utsname.hpp
-                        include/kernel/hw/acpi.hpp
-                        include/kernel/hw/pci.hpp
-                        include/kernel/hw/port.hpp
-                        include/types/list.hpp
-                        include/types/types.h
-                        include/types/allocator.hpp
-                        include/kernel/log.hpp
-                        )
-
-add_executable(kernel.out ${KERNEL_MAIN_SOURCES} ${BOOTLOADER_SOURCES})
-add_dependencies(kernel.out rustpart)
-target_link_libraries(kernel.out gblibc gblibstdc++ gbos_rust_part)
-target_include_directories(kernel.out PRIVATE ${PROJECT_SOURCE_DIR}/include)
-target_link_options(kernel.out PRIVATE
-    -T "${CMAKE_SOURCE_DIR}/src/kernel.ld"
-    -L "${CMAKE_BINARY_DIR}/x86_64-unknown-none/${CARGO_BUILD_TYPE}"
-    --no-check-sections
-    )
-set_target_properties(kernel.out PROPERTIES LINK_DEPENDS "${CMAKE_SOURCE_DIR}/src/kernel.ld")
-set_source_files_properties(src/mbr.S PROPERTIES OBJECT_DEPENDS
-    "${CMAKE_BINARY_DIR}/x86_64-unknown-none/${CARGO_BUILD_TYPE}/libgbos_rust_part.a"
-    )
-
-add_custom_target(rustpart
-    COMMAND cargo build ${CARGO_BUILD_FLAGS}
-)
-
-add_custom_command(OUTPUT mbr_hole.bin
-    DEPENDS kernel.out
-    COMMAND ${CMAKE_OBJCOPY} --strip-debug -O binary ${CMAKE_BINARY_DIR}/kernel.out mbr_hole.bin
-)
-
-add_custom_target(boot.img
-    DEPENDS mbr_hole.bin
-    DEPENDS user_space_programs
-    COMMAND make -C ${CMAKE_SOURCE_DIR} image
-)
-
-add_custom_command(OUTPUT run
-    POST_BUILD
-    DEPENDS boot.img
-    COMMAND bochs -f ${CMAKE_SOURCE_DIR}/bochs.conf
-)

+ 123 - 227
Cargo.lock

@@ -3,12 +3,14 @@
 version = 4
 
 [[package]]
-name = "aho-corasick"
-version = "1.1.3"
+name = "acpi"
+version = "5.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+checksum = "94476c7ef97af4c4d998b3f422c1b01d5211aad57c80ed200baf148d1f1efab6"
 dependencies = [
- "memchr",
+ "bit_field",
+ "bitflags",
+ "log",
 ]
 
 [[package]]
@@ -16,8 +18,17 @@ name = "arch"
 version = "0.1.0"
 dependencies = [
  "cfg-if",
+ "eonix_hal_traits",
  "eonix_mm",
- "percpu-macros",
+]
+
+[[package]]
+name = "arch_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
@@ -31,24 +42,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 
 [[package]]
-name = "bindgen"
-version = "0.70.1"
+name = "bit_field"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
-dependencies = [
- "bitflags",
- "cexpr",
- "clang-sys",
- "itertools",
- "log",
- "prettyplease",
- "proc-macro2",
- "quote",
- "regex",
- "rustc-hash",
- "shlex",
- "syn",
-]
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
 
 [[package]]
 name = "bitflags"
@@ -64,15 +61,6 @@ dependencies = [
  "intrusive_list",
 ]
 
-[[package]]
-name = "cexpr"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
-dependencies = [
- "nom",
-]
-
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
@@ -80,21 +68,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
-name = "clang-sys"
-version = "1.8.1"
+name = "either"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "eonix_hal"
+version = "0.1.0"
 dependencies = [
- "glob",
- "libc",
- "libloading",
+ "acpi",
+ "arch",
+ "cfg-if",
+ "eonix_hal_macros",
+ "eonix_hal_traits",
+ "eonix_mm",
+ "eonix_percpu",
+ "eonix_preempt",
+ "eonix_sync_base",
 ]
 
 [[package]]
-name = "either"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+name = "eonix_hal_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "eonix_hal_traits"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "eonix_mm",
+]
+
+[[package]]
+name = "eonix_kernel"
+version = "0.1.0"
+dependencies = [
+ "acpi",
+ "arch",
+ "atomic_unique_refcell",
+ "bitflags",
+ "buddy_allocator",
+ "eonix_hal",
+ "eonix_log",
+ "eonix_macros",
+ "eonix_mm",
+ "eonix_percpu",
+ "eonix_preempt",
+ "eonix_runtime",
+ "eonix_sync",
+ "intrusive-collections",
+ "intrusive_list",
+ "itertools",
+ "pointers",
+ "posix_types",
+ "slab_allocator",
+]
 
 [[package]]
 name = "eonix_log"
@@ -103,6 +137,15 @@ dependencies = [
  "eonix_sync",
 ]
 
+[[package]]
+name = "eonix_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "eonix_mm"
 version = "0.1.0"
@@ -110,11 +153,28 @@ dependencies = [
  "bitflags",
 ]
 
+[[package]]
+name = "eonix_percpu"
+version = "0.1.0"
+dependencies = [
+ "eonix_percpu_macros",
+]
+
+[[package]]
+name = "eonix_percpu_macros"
+version = "0.1.0"
+dependencies = [
+ "arch_macros",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "eonix_preempt"
 version = "0.1.0"
 dependencies = [
- "arch",
+ "eonix_percpu",
 ]
 
 [[package]]
@@ -123,7 +183,9 @@ version = "0.1.0"
 dependencies = [
  "arch",
  "atomic_unique_refcell",
+ "eonix_hal",
  "eonix_log",
+ "eonix_percpu",
  "eonix_preempt",
  "eonix_sync",
  "intrusive-collections",
@@ -131,42 +193,38 @@ dependencies = [
 ]
 
 [[package]]
-name = "eonix_sync"
+name = "eonix_spin"
 version = "0.1.0"
 dependencies = [
- "arch",
  "eonix_preempt",
- "intrusive-collections",
+ "eonix_sync_base",
+]
+
+[[package]]
+name = "eonix_sync"
+version = "0.1.0"
+dependencies = [
+ "eonix_spin",
+ "eonix_sync_base",
+ "eonix_sync_rt",
 ]
 
 [[package]]
-name = "gbos-rust-part"
+name = "eonix_sync_base"
+version = "0.1.0"
+
+[[package]]
+name = "eonix_sync_rt"
 version = "0.1.0"
 dependencies = [
  "arch",
- "atomic_unique_refcell",
- "bindgen",
- "bitflags",
- "buddy_allocator",
- "eonix_log",
- "eonix_mm",
+ "eonix_hal",
  "eonix_preempt",
- "eonix_runtime",
- "eonix_sync",
+ "eonix_spin",
+ "eonix_sync_base",
  "intrusive-collections",
- "intrusive_list",
- "itertools",
- "pointers",
- "posix_types",
- "slab_allocator",
 ]
 
-[[package]]
-name = "glob"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
-
 [[package]]
 name = "intrusive-collections"
 version = "0.9.7"
@@ -189,34 +247,12 @@ dependencies = [
  "either",
 ]
 
-[[package]]
-name = "libc"
-version = "0.2.164"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
-
-[[package]]
-name = "libloading"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
-dependencies = [
- "cfg-if",
- "windows-targets",
-]
-
 [[package]]
 name = "log"
 version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
-[[package]]
-name = "memchr"
-version = "2.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
-
 [[package]]
 name = "memoffset"
 version = "0.9.1"
@@ -226,31 +262,6 @@ dependencies = [
  "autocfg",
 ]
 
-[[package]]
-name = "minimal-lexical"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
-
-[[package]]
-name = "nom"
-version = "7.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
-dependencies = [
- "memchr",
- "minimal-lexical",
-]
-
-[[package]]
-name = "percpu-macros"
-version = "0.1.0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "pointers"
 version = "0.1.0"
@@ -259,16 +270,6 @@ version = "0.1.0"
 name = "posix_types"
 version = "0.1.0"
 
-[[package]]
-name = "prettyplease"
-version = "0.2.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
-dependencies = [
- "proc-macro2",
- "syn",
-]
-
 [[package]]
 name = "proc-macro2"
 version = "1.0.92"
@@ -287,47 +288,6 @@ dependencies = [
  "proc-macro2",
 ]
 
-[[package]]
-name = "regex"
-version = "1.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
-
-[[package]]
-name = "rustc-hash"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-
-[[package]]
-name = "shlex"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-
 [[package]]
 name = "slab_allocator"
 version = "0.1.0"
@@ -353,67 +313,3 @@ name = "unicode-ident"
 version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
-
-[[package]]
-name = "windows-targets"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
-dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

+ 7 - 6
Cargo.toml

@@ -1,10 +1,10 @@
 [package]
-name = "gbos-rust-part"
+name = "eonix_kernel"
 version = "0.1.0"
 edition = "2021"
 
 [lib]
-crate-type = ["staticlib"]
+crate-type = ["bin"]
 
 [dependencies]
 arch = { path = "./arch" }
@@ -12,8 +12,10 @@ atomic_unique_refcell = { path = "./crates/atomic_unique_refcell", features = [
     "no_std",
 ] }
 buddy_allocator = { path = "./crates/buddy_allocator" }
-slab_allocator = { path = "./crates/slab_allocator" }
+eonix_hal = { path = "./crates/eonix_hal" }
+eonix_macros = { path = "./macros" }
 eonix_mm = { path = "./crates/eonix_mm" }
+eonix_percpu = { path = "./crates/eonix_percpu" }
 eonix_preempt = { path = "./crates/eonix_preempt" }
 eonix_runtime = { path = "./crates/eonix_runtime" }
 eonix_sync = { path = "./crates/eonix_sync" }
@@ -21,10 +23,12 @@ eonix_log = { path = "./crates/eonix_log" }
 intrusive_list = { path = "./crates/intrusive_list" }
 pointers = { path = "./crates/pointers" }
 posix_types = { path = "./crates/posix_types" }
+slab_allocator = { path = "./crates/slab_allocator" }
 
 bitflags = "2.6.0"
 intrusive-collections = "0.9.7"
 itertools = { version = "0.13.0", default-features = false }
+acpi = "5.2.0"
 
 [features]
 default = ["smp"]
@@ -34,9 +38,6 @@ log_trace = ["trace_syscall", "trace_scheduler"]
 log_debug = []
 smp = []
 
-[build-dependencies]
-bindgen = "0.70.1"
-
 [profile.dev]
 panic = "abort"
 

+ 66 - 40
Makefile.src

@@ -1,42 +1,51 @@
-# disable kvm to debug triple faults
-QEMU_BIN=##PLACEHOLDER_1##
-GDB_BIN=##PLACEHOLDER_2##
-QEMU_ACCELERATION_FLAG=##PLACEHOLDER_3##
-QEMU_DEBUG_FLAG=#-d cpu_reset,int -D build/qemu.log
-QEMU_ARGS=-machine q35 -drive id=disk,file=build/boot.img,format=raw,if=none \
+HOST ?= $(shell uname -s | tr '[:upper:]' '[:lower:]')
+ARCH ?= ##DEFAULT_ARCH##
+MODE ?= debug
+SMP ?= 4
+
+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 -smp 4 \
-	-no-reboot -no-shutdown $(QEMU_ACCELERATION_FLAG) $(QEMU_DEBUG_FLAG)
+	-netdev user,id=mynet0,net=192.168.1.0/24,dhcpstart=192.168.1.16 -device e1000e,netdev=mynet0 \
+	-no-reboot -no-shutdown
+
+ifeq ($(HOST),darwin)
+QEMU_ACCEL ?= -accel tcg
+else ifeq ($(HOST),linux)
+QEMU_ACCEL ?= -accel kvm
+endif
+
+QEMU_ARGS += $(QEMU_ACCEL)
+
+ifneq ($(DEBUG_TRAPS),)
+QEMU_ARGS += -d cpu_reset,int,guest_errors -D build/qemu.log
+endif
 
-FDISK_BIN ?= ##PLACEHOLDER_5##
+ifneq ($(SMP),)
+QEMU_ARGS += -smp $(SMP)
+endif
+
+ifeq ($(MODE),debug)
+MODE := dev
+endif
 
-CROSS_COMPILE=##PLACEHOLDER_4##
 .PHONY: run
 run: build
-	$(QEMU_BIN) $(QEMU_ARGS) -display curses -S -s
+	$(QEMU) $(QEMU_ARGS) -display none -serial mon:stdio
+
 .PHONY: srun
 srun: build
-	$(QEMU_BIN) $(QEMU_ARGS) -display none -S -s -serial mon:stdio
-.PHONY: nativerun
-nativerun: build
-	$(QEMU_BIN) $(QEMU_ARGS) -display none -serial mon:stdio
-
-.PHONY: prepare
-prepare:
-	cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug $(CROSS_COMPILE)
-	cp build/compile_commands.json .
-
-.PHONY: reprepare
-reprepare: clean prepare
-	true
-
-.PHONY: build
-build:
-	cmake --build build -j 6 --target boot.img
+	$(QEMU) $(QEMU_ARGS) -display none -S -s -serial mon:stdio
 
 .PHONY: clean
 clean:
+	-mv build/fs.img .
 	-rm -rf build
-	-rm compile_commands.json
+	-mkdir build
+	-mv fs.img build
 
 .PHONY: clean-all
 clean-all: clean
@@ -44,14 +53,14 @@ clean-all: clean
 
 .PHONY: debug
 debug:
-	-$(GDB_BIN) --symbols=build/kernel.out \
+	-RUST_GDB=$(GDB) rust-gdb --symbols=build/kernel.sym \
 		-iex 'source pretty-print.py' \
 		-iex 'set pagination off' \
 		-iex 'set output-radix 16' \
 		-iex 'set print asm-demangle on' \
 		-iex 'set print pretty on' \
 		-iex 'target remote:1234'
-	-killall $(QEMU_BIN)
+	-killall $(QEMU)
 
 .PHONY: tmux-debug
 tmux-debug:
@@ -62,17 +71,34 @@ tmux-debug:
 	-tmux attach -t gbos-debug
 	tmux kill-session -t gbos-debug
 
+.PHONY: kernel
+kernel:
+	cargo build --profile $(MODE)
+
+build/kernel.sym: kernel
+	cargo objcopy --profile $(MODE) -- --only-keep-debug build/kernel.sym
+
+build/mbr.bin: kernel
+	cargo objcopy --profile $(MODE) -- -O binary -j .mbr build/mbr.bin
+
+build/stage1.bin: kernel
+	cargo objcopy --profile $(MODE) -- -O binary -j .stage1 build/stage1.bin
+
+build/kernel.bin: kernel
+	cargo objcopy --profile $(MODE) -- -O binary --strip-debug \
+		-R .mbr -R .stage1 build/kernel.bin
+
 build/fs.img: init_script.sh
 	sh script/build-img.sh
 
-build/boot.img: build/fs.img build/mbr_hole.bin
-	dd if=build/mbr_hole.bin of=build/boot.img
-	dd if=build/fs.img of=build/boot.img bs=$(shell expr 4 \* 1024 \* 1024) seek=1 conv=notrunc
-	sh -c 'echo n; echo; echo; echo 8192; echo; echo a; echo w' | $(FDISK_BIN) build/boot.img
+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) \
+		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
 
-build/boot.vdi: build/boot.img
-	-rm build/boot.vdi
-	VBoxManage convertfromraw $< $@ --format VDI
-
-.PHONY: image
-image: build/boot.img
+.PHONY: build
+build: build/boot.img build/kernel.sym

+ 19 - 14
README.md

@@ -15,7 +15,7 @@
 - [x] 静态 ELF 加载器
 - [x] TTY 及任务控制接口(正在进一步实现)
 - [x] FAT32 文件系统的读取实现
-- [ ] 全部 Rust 化(只剩一点点)
+- [x] 全部 Rust 化(只剩一点点)
 - [ ] 网卡驱动(WIP)
 - [ ] POSIX 线程接口(WIP)
 - [ ] 动态加载器(WIP)
@@ -180,11 +180,11 @@
 
 ```bash
 # 配置构建环境
-./configure && make prepare && make build
+./configure && make build
 
 # 直接运行
 
-make nativerun
+make run
 
 # 如果需要调试
 
@@ -201,14 +201,19 @@ make tmux-debug
 
 可能需要在运行 `./configure` 时在环境变量中指定正确版本的构建工具。
 
-- `QEMU`: 用于调试运行的 QEMU。默认使用 `qemu-system-x86_64`。
-- `GDB`: 用于 `make debug` 的 GDB。我们将默认查找 `gdb` 或者是 `x86_64-elf-gdb` 并检查支持的架构。
-- `FDISK_BIN`: 用于创建磁盘镜像分区表的 fdisk 可执行文件。默认使用 `fdisk`。
-
-如果正在进行交叉编译,请在运行 `./configure` 时将 `CROSS_COMPILE` 设置为你的交叉编译器的相应目标三元组。
-
-## 运行自己编译的程序
-
-项目目录下的 `user` 目录主要是出于一些*历史*原因存在,几乎没有任何用处。所以不要尝试查看里面的内容。
-
-要将你的程序(可以使用任何编译器为i386架构编译,静态链接)复制到构建的磁盘镜像中,你可以编辑 `CMakeLists.txt` 并在 `boot.img` 部分添加一行。你也可以尝试编辑 `init_script.sh` 以自定义启动过程。
+- `DEFAULT_ARCH`: 在调用 Makefile 时如果不进行额外指定,默认使用的架构。默认为 `x86_64`。
+- `QEMU`: 用于调试运行的 QEMU。默认使用 `qemu-system-$(ARCH)`。
+- `GDB`: 用于 `make debug` 的 GDB。我们将默认查找 `$(ARCH)-elf-gdb` 并检查支持的架构。
+- `FDISK`: 用于创建磁盘镜像分区表的 fdisk 可执行文件,要求使用来自 util-linux 版本的 fdisk。默认使用 `fdisk`。
+
+在运行 make 时可以指定的额外选项:
+
+- `HOST`: 当前平台架构,用于决定 qemu 的默认加速模式,默认使用 `uname -s` 的输出。
+- `ARCH`: 编译运行的目标架构,默认使用 configure 时指定的值。
+- `MODE`: 编译运行的模式,可以使用 `debug` 或者是 `release`。
+- `SMP`: 是否运行多处理器处理,默认使用 4 CPU。
+- `QEMU`: 手动指定 qemu 路径。
+- `GDB`: 手动指定 gdb 路径。
+- `FDISK`: 手动指定 fdisk 路径。
+- `QEMU_ACCEL`: 手动指定要使用的 qemu 加速方法。
+- `DEBUG_TRAPS`: 是否要进行 trap 的调试,使 qemu 输出详细的 trap 日志。

+ 1 - 1
arch/Cargo.toml

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

+ 9 - 0
arch/arch_macros/Cargo.toml

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

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

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

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

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

+ 32 - 0
arch/arch_macros/src/x86_64/percpu.rs

@@ -0,0 +1,32 @@
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{Ident, LitStr, Type};
+
+/// Generate the assembly code to load the address of a symbol and add it to a register.
+pub fn load_addr_of_to(symbol: &LitStr, target: &LitStr) -> TokenStream {
+    quote! {
+        concat!("mov %gs:0, ", #target),
+        concat!("add $", #symbol, ", ", #target)
+    }
+}
+
+/// Get the base address for percpu variables of the current thread.
+pub fn get_percpu_pointer(percpu: &Ident, ty: &Type) -> TokenStream {
+    let stmt = load_addr_of_to(
+        &LitStr::new("{ident}", Span::call_site()),
+        &LitStr::new("{address}", Span::call_site()),
+    );
+
+    quote! {
+        {
+            let base: *mut #ty;
+            ::core::arch::asm!(
+                #stmt,
+                ident = sym #percpu,
+                address = out(reg) base,
+                options(att_syntax, nostack, preserves_flags)
+            );
+            base
+        }
+    }
+}

+ 0 - 47
arch/percpu-macros/Cargo.lock

@@ -1,47 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[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 - 24
arch/percpu-macros/src/arch.rs

@@ -1,24 +0,0 @@
-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! {
-        {
-            #[cfg(target_arch = "x86_64")]
-            {
-                let base: *mut #ty;
-                ::core::arch::asm!(
-                    "mov %gs:0, {address}",
-                    "add ${percpu_pointer}, {address}",
-                    percpu_pointer = sym #percpu,
-                    address = out(reg) base,
-                    options(att_syntax)
-                );
-                base
-            }
-        }
-    }
-    .into()
-}

+ 0 - 1
arch/src/lib.rs

@@ -1,5 +1,4 @@
 #![no_std]
-#![feature(naked_functions)]
 
 cfg_if::cfg_if! {
     if #[cfg(target_arch = "x86_64")] {

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

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

+ 0 - 555
arch/src/x86_64/interrupt.rs

@@ -1,555 +0,0 @@
-use core::{
-    arch::{asm, global_asm},
-    pin::Pin,
-    ptr::NonNull,
-};
-
-use crate::rdmsr;
-
-use super::pause;
-
-global_asm!(
-    r"
-    .set RAX, 0x00
-    .set RBX, 0x08
-    .set RCX, 0x10
-    .set RDX, 0x18
-    .set RDI, 0x20
-    .set RSI, 0x28
-    .set R8, 0x30
-    .set R9, 0x38
-    .set R10, 0x40
-    .set R11, 0x48
-    .set R12, 0x50
-    .set R13, 0x58
-    .set R14, 0x60
-    .set R15, 0x68
-    .set RBP, 0x70
-    .set INT_NO, 0x78
-    .set ERRCODE, 0x80
-    .set RIP, 0x88
-    .set CS, 0x90
-    .set FLAGS, 0x98
-    .set RSP, 0xa0
-    .set SS, 0xa8
-
-    .macro movcfi reg, offset
-        mov \reg, \offset(%rsp)
-        .cfi_rel_offset \reg, \offset
-    .endm
-
-    .macro movrst reg, offset
-        mov \offset(%rsp), \reg
-        .cfi_restore \reg
-    .endm
-
-    .globl ISR_stub_restore
-    .type ISR_stub_restore @function
-
-    ISR_stub:
-        .cfi_startproc
-        .cfi_signal_frame
-        .cfi_def_cfa_offset 0x18
-        .cfi_offset %rsp, 0x10
-
-        cmpq $0x08, 24(%rsp)
-        je 1f
-        swapgs
-
-    1:
-        sub $0x78, %rsp
-        .cfi_def_cfa_offset 0x90
-
-        movcfi %rax, RAX
-        movcfi %rbx, RBX
-        movcfi %rcx, RCX
-        movcfi %rdx, RDX
-        movcfi %rdi, RDI
-        movcfi %rsi, RSI
-        movcfi %r8,  R8
-        movcfi %r9,  R9
-        movcfi %r10, R10
-        movcfi %r11, R11
-        movcfi %r12, R12
-        movcfi %r13, R13
-        movcfi %r14, R14
-        movcfi %r15, R15
-        movcfi %rbp, RBP
-
-        mov INT_NO(%rsp), %rax
-        sub $ISR0, %rax
-        shr $3, %rax
-        mov %rax, INT_NO(%rsp)
-
-        mov %rsp, %rbx
-        .cfi_def_cfa_register %rbx
-
-        and $~0xf, %rsp
-        sub $512, %rsp
-        fxsave (%rsp)
-
-        mov %rbx, %rdi
-        mov %rsp, %rsi
-        call interrupt_handler
-
-    ISR_stub_restore:
-        fxrstor (%rsp)
-        mov %rbx, %rsp
-        .cfi_def_cfa_register %rsp
-
-    .globl _arch_fork_return
-    _arch_fork_return:
-        movrst %rax, RAX
-        movrst %rbx, RBX
-        movrst %rcx, RCX
-        movrst %rdx, RDX
-        movrst %rdi, RDI
-        movrst %rsi, RSI
-        movrst %r8,  R8
-        movrst %r9,  R9
-        movrst %r10, R10
-        movrst %r11, R11
-        movrst %r12, R12
-        movrst %r13, R13
-        movrst %r14, R14
-        movrst %r15, R15
-        movrst %rbp, RBP
-
-        add $0x88, %rsp
-        .cfi_def_cfa_offset 0x08
-
-        cmpq $0x08, 8(%rsp)
-        je 1f
-        swapgs
-
-    1:
-        iretq
-        .cfi_endproc
-
-    .altmacro
-    .macro build_isr_no_err name
-        .align 8
-        .globl ISR\name
-        .type  ISR\name @function
-        ISR\name:
-            .cfi_startproc
-            .cfi_signal_frame
-            .cfi_def_cfa_offset 0x08
-            .cfi_offset %rsp, 0x10
-
-            .cfi_same_value %rax
-            .cfi_same_value %rbx
-            .cfi_same_value %rcx
-            .cfi_same_value %rdx
-            .cfi_same_value %rdi
-            .cfi_same_value %rsi
-            .cfi_same_value %r8
-            .cfi_same_value %r9
-            .cfi_same_value %r10
-            .cfi_same_value %r11
-            .cfi_same_value %r12
-            .cfi_same_value %r13
-            .cfi_same_value %r14
-            .cfi_same_value %r15
-            .cfi_same_value %rbp
-
-            push %rbp # push placeholder for error code
-            .cfi_def_cfa_offset 0x10
-
-            call ISR_stub
-            .cfi_endproc
-    .endm
-
-    .altmacro
-    .macro build_isr_err name
-        .align 8
-        .globl ISR\name
-        .type  ISR\name @function
-        ISR\name:
-            .cfi_startproc
-            .cfi_signal_frame
-            .cfi_def_cfa_offset 0x10
-            .cfi_offset %rsp, 0x10
-
-            .cfi_same_value %rax
-            .cfi_same_value %rbx
-            .cfi_same_value %rcx
-            .cfi_same_value %rdx
-            .cfi_same_value %rdi
-            .cfi_same_value %rsi
-            .cfi_same_value %r8
-            .cfi_same_value %r9
-            .cfi_same_value %r10
-            .cfi_same_value %r11
-            .cfi_same_value %r12
-            .cfi_same_value %r13
-            .cfi_same_value %r14
-            .cfi_same_value %r15
-            .cfi_same_value %rbp
-
-            call ISR_stub
-            .cfi_endproc
-    .endm
-
-    build_isr_no_err 0
-    build_isr_no_err 1
-    build_isr_no_err 2
-    build_isr_no_err 3
-    build_isr_no_err 4
-    build_isr_no_err 5
-    build_isr_no_err 6
-    build_isr_no_err 7
-    build_isr_err    8
-    build_isr_no_err 9
-    build_isr_err    10
-    build_isr_err    11
-    build_isr_err    12
-    build_isr_err    13
-    build_isr_err    14
-    build_isr_no_err 15
-    build_isr_no_err 16
-    build_isr_err    17
-    build_isr_no_err 18
-    build_isr_no_err 19
-    build_isr_no_err 20
-    build_isr_err    21
-    build_isr_no_err 22
-    build_isr_no_err 23
-    build_isr_no_err 24
-    build_isr_no_err 25
-    build_isr_no_err 26
-    build_isr_no_err 27
-    build_isr_no_err 28
-    build_isr_err    29
-    build_isr_err    30
-    build_isr_no_err 31
-
-    .set i, 32
-    .rept 0x80+1
-        build_isr_no_err %i
-        .set i, i+1
-    .endr
-
-    .section .rodata
-
-    .align 8
-    .globl ISR_START_ADDR
-    .type  ISR_START_ADDR @object
-    ISR_START_ADDR:
-        .quad ISR0
-    ",
-    options(att_syntax),
-);
-
-/// Saved registers when a trap (interrupt or exception) occurs.
-#[allow(missing_docs)]
-#[repr(C)]
-#[derive(Debug, Default, Clone, Copy)]
-pub struct InterruptContext {
-    pub rax: u64,
-    pub rbx: u64,
-    pub rcx: u64,
-    pub rdx: u64,
-    pub rdi: u64,
-    pub rsi: u64,
-    pub r8: u64,
-    pub r9: u64,
-    pub r10: u64,
-    pub r11: u64,
-    pub r12: u64,
-    pub r13: u64,
-    pub r14: u64,
-    pub r15: u64,
-    pub rbp: u64,
-
-    pub int_no: u64,
-    pub error_code: u64,
-
-    // Pushed by CPU
-    pub rip: u64,
-    pub cs: u64,
-    pub eflags: u64,
-    pub rsp: u64,
-    pub ss: u64,
-}
-
-#[allow(missing_docs)]
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub struct ExtendedContext {
-    /// For FPU states
-    data: [u8; 512],
-}
-
-#[repr(C)]
-#[derive(Clone, Copy)]
-struct IDTEntry {
-    offset_low: u16,
-    selector: u16,
-
-    interrupt_stack: u8,
-    attributes: u8,
-
-    offset_mid: u16,
-    offset_high: u32,
-    reserved: u32,
-}
-
-pub struct APICReg(*mut u32);
-pub struct APICRegs {
-    base: NonNull<u32>,
-}
-
-/// Architecture-specific interrupt control block.
-pub struct InterruptControl {
-    idt: [IDTEntry; 256],
-    apic_base: APICRegs,
-}
-
-/// State of the interrupt flag.
-pub struct IrqState(u64);
-
-impl InterruptContext {
-    pub fn set_return_value(&mut self, value: u64) {
-        // The return value is stored in rax.
-        self.rax = value;
-    }
-
-    pub fn set_return_address(&mut self, addr: u64, user: bool) {
-        // The return address is stored in rip.
-        self.rip = addr;
-        if user {
-            self.cs = 0x2b; // User code segment
-        } else {
-            self.cs = 0x08; // Kernel code segment
-        }
-    }
-
-    pub fn set_stack_pointer(&mut self, sp: u64, user: bool) {
-        // The stack pointer is stored in rsp.
-        self.rsp = sp;
-        if user {
-            self.ss = 0x33; // User stack segment
-        } else {
-            self.ss = 0x10; // Kernel stack segment
-        }
-    }
-
-    pub fn set_interrupt_enabled(&mut self, enabled: bool) {
-        // The interrupt state is stored in eflags.
-        if enabled {
-            self.eflags |= 0x200; // Set the interrupt flag
-        } else {
-            self.eflags &= !0x200; // Clear the interrupt flag
-        }
-    }
-}
-
-impl IDTEntry {
-    const fn new(offset: usize, selector: u16, attributes: u8) -> Self {
-        Self {
-            offset_low: offset as u16,
-            selector,
-            interrupt_stack: 0,
-            attributes,
-            offset_mid: (offset >> 16) as u16,
-            offset_high: (offset >> 32) as u32,
-            reserved: 0,
-        }
-    }
-
-    const fn null() -> Self {
-        Self {
-            offset_low: 0,
-            selector: 0,
-            interrupt_stack: 0,
-            attributes: 0,
-            offset_mid: 0,
-            offset_high: 0,
-            reserved: 0,
-        }
-    }
-}
-
-impl APICReg {
-    fn new(pointer: *mut u32) -> Self {
-        Self(pointer)
-    }
-
-    pub fn read(&self) -> u32 {
-        unsafe { self.0.read_volatile() }
-    }
-
-    pub fn write(&self, value: u32) {
-        unsafe { self.0.write_volatile(value) }
-    }
-}
-
-impl APICRegs {
-    pub fn local_apic_id(&self) -> APICReg {
-        unsafe { APICReg::new(self.base.byte_offset(0x20).as_ptr()) }
-    }
-
-    pub fn task_priority(&self) -> APICReg {
-        unsafe { APICReg::new(self.base.byte_offset(0x80).as_ptr()) }
-    }
-
-    pub fn end_of_interrupt(&self) {
-        unsafe { APICReg::new(self.base.byte_offset(0xb0).as_ptr()).write(0) }
-    }
-
-    pub fn spurious(&self) -> APICReg {
-        unsafe { APICReg::new(self.base.byte_offset(0xf0).as_ptr()) }
-    }
-
-    pub fn interrupt_command(&self) -> APICReg {
-        unsafe { APICReg::new(self.base.byte_offset(0x300).as_ptr()) }
-    }
-
-    pub fn timer_register(&self) -> APICReg {
-        unsafe { APICReg::new(self.base.byte_offset(0x320).as_ptr()) }
-    }
-
-    pub fn timer_initial_count(&self) -> APICReg {
-        unsafe { APICReg::new(self.base.byte_offset(0x380).as_ptr()) }
-    }
-
-    pub fn timer_current_count(&self) -> APICReg {
-        unsafe { APICReg::new(self.base.byte_offset(0x390).as_ptr()) }
-    }
-
-    pub fn timer_divide(&self) -> APICReg {
-        unsafe { APICReg::new(self.base.byte_offset(0x3e0).as_ptr()) }
-    }
-}
-
-impl InterruptControl {
-    /// # Return
-    /// Returns a tuple of InterruptControl and the cpu id of the current cpu.
-    pub(crate) fn new() -> (Self, usize) {
-        extern "C" {
-            static ISR_START_ADDR: usize;
-        }
-
-        let idt = core::array::from_fn(|idx| match idx {
-            0..0x80 => IDTEntry::new(unsafe { ISR_START_ADDR } + 8 * idx, 0x08, 0x8e),
-            0x80 => IDTEntry::new(unsafe { ISR_START_ADDR } + 8 * idx, 0x08, 0xee),
-            _ => IDTEntry::null(),
-        });
-
-        let apic_base = {
-            let apic_base = rdmsr(0x1b);
-            assert_eq!(apic_base & 0x800, 0x800, "LAPIC not enabled");
-
-            let apic_base = ((apic_base & !0xfff) + 0xffffff00_00000000) as *mut u32;
-            APICRegs {
-                // TODO: A better way to convert to physical address
-                base: NonNull::new(apic_base).expect("Invalid APIC base"),
-            }
-        };
-
-        // Make sure APIC is enabled.
-        apic_base.spurious().write(0x1ff);
-
-        let cpuid = apic_base.local_apic_id().read() >> 24;
-
-        (Self { idt, apic_base }, cpuid as usize)
-    }
-
-    pub fn setup_timer(&self) {
-        self.apic_base.task_priority().write(0);
-        self.apic_base.timer_divide().write(0x3); // Divide by 16
-        self.apic_base.timer_register().write(0x20040);
-
-        // TODO: Get the bus frequency from...?
-        let freq = 200;
-        let count = freq * 1_000_000 / 16 / 100;
-        self.apic_base.timer_initial_count().write(count as u32);
-    }
-
-    pub fn setup_idt(self: Pin<&mut Self>) {
-        lidt(
-            self.idt.as_ptr() as usize,
-            (size_of::<IDTEntry>() * 256 - 1) as u16,
-        );
-    }
-
-    pub fn send_sipi(&self) {
-        let icr = self.apic_base.interrupt_command();
-
-        icr.write(0xc4500);
-        while icr.read() & 0x1000 != 0 {
-            pause();
-        }
-
-        icr.write(0xc4601);
-        while icr.read() & 0x1000 != 0 {
-            pause();
-        }
-    }
-
-    /// Send EOI to APIC so that it can send more interrupts.
-    pub fn end_of_interrupt(&self) {
-        self.apic_base.end_of_interrupt()
-    }
-}
-
-impl IrqState {
-    pub fn restore(self) {
-        let Self(state) = self;
-
-        unsafe {
-            asm!(
-                "push {state}",
-                "popf",
-                state = in(reg) state,
-                options(att_syntax, nomem)
-            );
-        }
-    }
-}
-
-pub fn enable_irqs() {
-    unsafe {
-        asm!("sti", options(att_syntax, nomem, nostack));
-    }
-}
-
-pub fn disable_irqs() {
-    unsafe {
-        asm!("cli", options(att_syntax, nomem, nostack));
-    }
-}
-
-pub fn disable_irqs_save() -> IrqState {
-    let state: u64;
-    unsafe {
-        asm!(
-            "pushf",
-            "pop {state}",
-            "cli",
-            state = out(reg) state,
-            options(att_syntax, nomem)
-        );
-    }
-
-    IrqState(state)
-}
-
-extern "C" {
-    pub fn _arch_fork_return();
-}
-
-fn lidt(base: usize, limit: u16) {
-    let mut idt_descriptor = [0u16; 5];
-
-    idt_descriptor[0] = limit;
-    idt_descriptor[1] = base as u16;
-    idt_descriptor[2] = (base >> 16) as u16;
-    idt_descriptor[3] = (base >> 32) as u16;
-    idt_descriptor[4] = (base >> 48) as u16;
-
-    unsafe {
-        asm!("lidt ({})", in(reg) &idt_descriptor, options(att_syntax));
-    }
-}

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

@@ -1,22 +1,5 @@
 use core::arch::asm;
 
-pub fn enable_sse() {
-    unsafe {
-        asm!(
-            "mov %cr0, %rax",
-            "and $(~0xc), %rax",
-            "or $0x22, %rax",
-            "mov %rax, %cr0",
-            "mov %cr4, %rax",
-            "or $0x600, %rax",
-            "mov %rax, %cr4",
-            "fninit",
-            out("rax") _,
-            options(att_syntax, nomem, nostack)
-        )
-    }
-}
-
 pub fn inb(no: u16) -> u8 {
     let data;
     unsafe {

+ 0 - 202
arch/src/x86_64/mm.rs

@@ -1,202 +0,0 @@
-use core::{marker::PhantomData, ptr::NonNull};
-use eonix_mm::{
-    address::{Addr as _, PAddr},
-    page_table::{
-        PageAttribute, PageTableLevel, PagingMode, RawAttribute, RawPageTable, TableAttribute, PTE,
-    },
-    paging::{PageBlock, PFN},
-};
-
-pub const PAGE_SIZE: usize = 0x1000;
-
-const KERNEL_PML4_PFN: PFN = PFN::from_val(0x2000 >> 12);
-
-const PA_P: u64 = 0x001;
-const PA_RW: u64 = 0x002;
-const PA_US: u64 = 0x004;
-#[allow(dead_code)]
-const PA_PWT: u64 = 0x008;
-#[allow(dead_code)]
-const PA_PCD: u64 = 0x010;
-const PA_A: u64 = 0x020;
-const PA_D: u64 = 0x040;
-const PA_PS: u64 = 0x080;
-const PA_G: u64 = 0x100;
-const PA_COW: u64 = 0x200;
-const PA_MMAP: u64 = 0x400;
-const PA_ANON: u64 = 0x800;
-const PA_NXE: u64 = 0x8000_0000_0000_0000;
-const PA_MASK: u64 = 0xfff0_0000_0000_0fff;
-
-#[repr(transparent)]
-pub struct PTE64(u64);
-
-#[derive(Clone, Copy)]
-pub struct PageAttribute64(u64);
-
-pub struct RawPageTable4Levels<'a>(NonNull<PTE64>, PhantomData<&'a ()>);
-
-pub struct PagingMode4Levels;
-
-impl PTE for PTE64 {
-    type Attr = PageAttribute64;
-
-    fn set(&mut self, pfn: PFN, attr: Self::Attr) {
-        let paddr = PAddr::from(pfn).addr();
-
-        self.0 = (paddr 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),
-        )
-    }
-}
-
-impl PagingMode for PagingMode4Levels {
-    type Entry = PTE64;
-    type RawTable<'a> = RawPageTable4Levels<'a>;
-
-    const LEVELS: &'static [PageTableLevel] = &[
-        PageTableLevel::new(39, 9),
-        PageTableLevel::new(30, 9),
-        PageTableLevel::new(21, 9),
-        PageTableLevel::new(12, 9),
-    ];
-
-    const KERNEL_ROOT_TABLE_PFN: PFN = KERNEL_PML4_PFN;
-}
-
-impl<'a> RawPageTable<'a> for RawPageTable4Levels<'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 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_PS != 0 {
-            panic!("Encountered a huge page while parsing table attributes");
-        }
-
-        if self.0 & PA_P != 0 {
-            table_attr |= TableAttribute::PRESENT;
-        }
-        if self.0 & PA_G != 0 {
-            table_attr |= TableAttribute::GLOBAL;
-        }
-        if self.0 & PA_US != 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::READ;
-
-        if self.0 & PA_P != 0 {
-            page_attr |= PageAttribute::PRESENT;
-        }
-
-        if self.0 & PA_RW != 0 {
-            page_attr |= PageAttribute::WRITE;
-        }
-
-        if self.0 & PA_NXE == 0 {
-            page_attr |= PageAttribute::EXECUTE;
-        }
-
-        if self.0 & PA_US != 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)
-    }
-
-    fn from_table_attr(table_attr: TableAttribute) -> Self {
-        let mut raw_attr = PA_RW;
-
-        for attr in table_attr.iter() {
-            match attr {
-                TableAttribute::PRESENT => raw_attr |= PA_P,
-                TableAttribute::GLOBAL => raw_attr |= PA_G,
-                TableAttribute::USER => raw_attr |= PA_US,
-                TableAttribute::ACCESSED => raw_attr |= PA_A,
-                _ => unreachable!("Invalid table attribute"),
-            }
-        }
-
-        Self(raw_attr)
-    }
-
-    fn from_page_attr(page_attr: PageAttribute) -> Self {
-        let mut raw_attr = PA_NXE;
-
-        for attr in page_attr.iter() {
-            match attr {
-                PageAttribute::PRESENT => raw_attr |= PA_P,
-                PageAttribute::READ => {}
-                PageAttribute::WRITE => raw_attr |= PA_RW,
-                PageAttribute::EXECUTE => raw_attr &= !PA_NXE,
-                PageAttribute::USER => raw_attr |= PA_US,
-                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 => raw_attr |= PA_ANON,
-                _ => unreachable!("Invalid page attribute"),
-            }
-        }
-
-        Self(raw_attr)
-    }
-}
-
-pub type DefaultPagingMode = PagingMode4Levels;

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

@@ -1,27 +1,14 @@
-mod context;
 mod fence;
-mod gdt;
-mod init;
-mod interrupt;
+mod fpu;
 mod io;
-mod mm;
-mod percpu;
-mod user;
 
 use core::arch::asm;
 use eonix_mm::address::{Addr as _, PAddr, VAddr};
 use eonix_mm::paging::PFN;
 
-pub use self::context::*;
-pub use self::gdt::*;
-pub use self::init::*;
-pub use self::interrupt::*;
 pub use self::io::*;
-pub use self::user::*;
 pub use fence::*;
-pub use mm::*;
-pub use percpu::*;
-pub use percpu_macros::{define_percpu, define_percpu_shared};
+pub use fpu::*;
 
 #[inline(always)]
 pub fn flush_tlb(vaddr: usize) {
@@ -97,14 +84,6 @@ pub fn pause() {
     }
 }
 
-#[inline(always)]
-pub fn freeze() -> ! {
-    loop {
-        interrupt::disable_irqs();
-        halt();
-    }
-}
-
 #[inline(always)]
 pub fn rdmsr(msr: u32) -> u64 {
     let edx: u32;

+ 0 - 86
arch/src/x86_64/percpu.rs

@@ -1,86 +0,0 @@
-use super::wrmsr;
-use crate::x86_64::mm::PAGE_SIZE;
-use core::{
-    alloc::Layout,
-    arch::asm,
-    cell::UnsafeCell,
-    ptr::{null_mut, NonNull},
-    sync::atomic::{AtomicPtr, Ordering},
-};
-
-pub const MAX_CPUS: usize = 256;
-
-#[repr(align(4096))]
-struct PercpuData(UnsafeCell<()>); // Not `Sync`.
-
-pub struct PercpuArea {
-    data: NonNull<PercpuData>,
-}
-
-static PERCPU_POINTERS: [AtomicPtr<PercpuData>; MAX_CPUS] =
-    [const { AtomicPtr::new(null_mut()) }; MAX_CPUS];
-
-impl PercpuArea {
-    fn page_count() -> usize {
-        extern "C" {
-            static PERCPU_PAGES: usize;
-        }
-        // SAFETY: `PERCPU_PAGES` is defined in linker script and never change.
-        let page_count = unsafe { PERCPU_PAGES };
-        assert_ne!(page_count, 0);
-        page_count
-    }
-
-    fn data_start() -> NonNull<u8> {
-        extern "C" {
-            fn _PERCPU_DATA_START();
-        }
-
-        NonNull::new(_PERCPU_DATA_START as usize as *mut _)
-            .expect("Percpu data should not be null.")
-    }
-
-    fn layout() -> Layout {
-        Layout::from_size_align(Self::page_count() * PAGE_SIZE, PAGE_SIZE).expect("Invalid layout.")
-    }
-
-    pub fn new<F>(allocate: F) -> Self
-    where
-        F: FnOnce(Layout) -> NonNull<u8>,
-    {
-        let data_pointer = allocate(Self::layout());
-
-        unsafe {
-            // SAFETY: The `data_pointer` is of valid length and properly aligned.
-            data_pointer
-                .copy_from_nonoverlapping(Self::data_start(), Self::page_count() * PAGE_SIZE);
-        }
-
-        Self {
-            data: data_pointer.cast(),
-        }
-    }
-
-    /// Set up the percpu area for the current CPU.
-    pub fn setup(&self) {
-        wrmsr(0xC0000101, self.data.as_ptr() as u64);
-
-        unsafe {
-            // SAFETY: %gs:0 points to the start of the percpu area.
-            asm!(
-                "movq {}, %gs:0",
-                in(reg) self.data.as_ptr(),
-                options(nostack, preserves_flags, att_syntax)
-            );
-        }
-    }
-
-    pub fn register(self: Self, cpuid: usize) {
-        PERCPU_POINTERS[cpuid].store(self.data.as_ptr(), Ordering::Release);
-    }
-
-    pub fn get_for(cpuid: usize) -> Option<NonNull<()>> {
-        let pointer = PERCPU_POINTERS[cpuid].load(Ordering::Acquire);
-        NonNull::new(pointer.cast())
-    }
-}

+ 0 - 54
arch/src/x86_64/user.rs

@@ -1,54 +0,0 @@
-use core::pin::Pin;
-
-use super::{CPU, GDTEntry};
-
-#[derive(Debug, Clone)]
-pub enum UserTLS {
-    /// TODO: This is not used yet.
-    #[allow(dead_code)]
-    TLS64(u64),
-    TLS32 {
-        base: u64,
-        desc: GDTEntry,
-    },
-}
-
-impl UserTLS {
-    /// # Return
-    /// Returns the TLS descriptor and the index of the TLS segment.
-    pub fn new32(base: u32, limit: u32, is_limit_in_pages: bool) -> (Self, u32) {
-        let flags = if is_limit_in_pages { 0xc } else { 0x4 };
-
-        (
-            Self::TLS32 {
-                base: base as u64,
-                desc: GDTEntry::new(base, limit, 0xf2, flags),
-            },
-            7,
-        )
-    }
-
-    pub fn load(&self, cpu_status: Pin<&mut CPU>) {
-        match self {
-            Self::TLS64(base) => {
-                const IA32_KERNEL_GS_BASE: u32 = 0xc0000102;
-                super::wrmsr(IA32_KERNEL_GS_BASE, *base);
-            }
-            Self::TLS32 { base, desc } => {
-                unsafe {
-                    // SAFETY: We don't move the CPUStatus object.
-                    let cpu_mut = cpu_status.get_unchecked_mut();
-                    cpu_mut.set_tls32(*desc);
-                }
-
-                const IA32_KERNEL_GS_BASE: u32 = 0xc0000102;
-                super::wrmsr(IA32_KERNEL_GS_BASE, *base);
-            }
-        }
-    }
-}
-
-pub unsafe fn load_interrupt_stack(cpu_status: Pin<&mut CPU>, stack: u64) {
-    // SAFETY: We don't move the CPUStatus object.
-    cpu_status.get_unchecked_mut().set_rsp0(stack);
-}

+ 8 - 26
build.rs

@@ -1,28 +1,10 @@
-fn main() {
-    println!("cargo:rustc-link-search=native=./build/gblibstdc++");
-    println!("cargo:rustc-link-lib=static=gblibstdc++");
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    println!("cargo:rustc-link-arg=-T{}", "link.x");
+    if let Ok(extra_link_args) = std::env::var("DEP_EONIX_HAL_EXTRA_LINK_ARGS") {
+        for arg in extra_link_args.split_whitespace() {
+            println!("cargo:rustc-link-arg={}", arg);
+        }
+    }
 
-    let headers = [
-        "rust-headers.hpp",
-        "include/kernel/hw/pci.hpp",
-    ];
-
-    let bindings = bindgen::Builder::default()
-        .use_core()
-        .ctypes_prefix("core::ffi")
-        .headers(headers)
-        .clang_arg("-I./gblibstdc++/include")
-        .clang_arg("-I./gblibc/include")
-        .clang_arg("-I./include")
-        .clang_arg("-std=c++20")
-        .opaque_type("std::.*")
-        .enable_cxx_namespaces()
-        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
-        .generate()
-        .expect("Unable to generate bindings");
-
-    let out_path = std::path::PathBuf::from(std::env::var("PWD").unwrap());
-    bindings
-        .write_to_file(out_path.join("src/bindings.rs"))
-        .expect("Couldn't write bindings!");
+    Ok(())
 }

+ 39 - 90
configure

@@ -1,41 +1,27 @@
 #!/bin/sh
-QEMU_EXECUTABLES="qemu-system-x86_64"
-GDB_EXECUTABLES="gdb x86_64-elf-gdb"
+DEFAULT_ARCH="x86_64"
 
 event() {
     printf "$1... "
 }
 
-# $1: OS NAME
-# $2: CROSS COMPILE FLAG
-# $3: FDISK_BIN
-generate_cross_compile_script() {
-cat > cross-compile.cmake <<EOF
-set(CMAKE_SYSTEM_NAME $1)
-
-set(TOOLCHAIN_PATH_AND_PREFIX "$2")
-
-set(CMAKE_C_COMPILER \${TOOLCHAIN_PATH_AND_PREFIX}gcc)
-set(CMAKE_CXX_COMPILER \${TOOLCHAIN_PATH_AND_PREFIX}c++)
-set(CMAKE_AR \${TOOLCHAIN_PATH_AND_PREFIX}ar)
-set(CMAKE_LINKER \${TOOLCHAIN_PATH_AND_PREFIX}ld)
-set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
-
-SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-# for libraries and headers in the target directories
-SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-SET(FDISK_BIN $3)
-EOF
-}
-
-event "finding qemu"
-for item in $QEMU_EXECUTABLES; do
-    if $item --version > /dev/null 2>&1; then
-        QEMU="$item"
+if [ "$QEMU" = "" ]; then
+    event "checking default qemu"
+    QEMU="qemu-system-$DEFAULT_ARCH"
+    if $QEMU --version > /dev/null 2>&1; then
+        QEMU="qemu-system-\$(ARCH)"
         break
     fi
-done
+else
+    event "checking given qemu"
+    for item in $QEMU; do
+        if $item --version > /dev/null 2>&1; then
+            QEMU="$item"
+            break
+        fi
+    done
+    QEMU=""
+fi
 if [ "$QEMU" = "" ]; then
     echo "failed"
     exit 1
@@ -46,7 +32,7 @@ check_gdb_arch() {
     local item="$1"
     if $item --init-eval-command 'set arch' \
              --init-eval-command 'q' 2>&1 \
-             | grep 'x86-64' >/dev/null 2>&1; then
+             | grep "$DEFAULT_ARCH" >/dev/null 2>&1; then
         return 0
     else
         return 1
@@ -54,77 +40,41 @@ check_gdb_arch() {
 }
 
 if [ "$GDB" = "" ]; then
-    event "finding gdb"
-    for item in $GDB_EXECUTABLES; do
-        if check_gdb_arch "$item"; then
+    event "checking default gdb"
+    if check_gdb_arch "$DEFAULT_ARCH-elf-gdb"; then
+        GDB="\$(ARCH)-elf-gdb"
+        break
+    fi
+else
+    event 'checking given gdb'
+    for item in $GDB; do
+        if check_gdb_arch "$GDB"; then
             GDB="$item"
             break
         fi
     done
-    if [ "$GDB" = "" ]; then
-        echo "failed"
-        exit 1
-    fi
-else
-    event 'checking given gdb'
-    if ! check_gdb_arch "$GDB"; then
-        echo "failed"
-        exit 1
-    fi
+    GDB=""
+fi
+if [ "$GDB" = "" ]; then
+    echo "failed"
+    exit 1
 fi
 echo "$GDB"
 
-event "checking os type"
-OS=`uname`
-case "$OS" in
-    "Linux")
-        echo "Linux"
-        ;;
-    "Darwin")
-        echo "macOS"
-        ;;
-    *)
-        echo "unknown"
-        exit 1
-        ;;
-esac
-
-event "setting hardware acceleration type"
-case "$OS" in
-    "Linux")
-        echo "kvm"
-        # QEMU_ACCEL='-enable-kvm'
-        ;;
-    "Darwin")
-        echo "tcg"
-        QEMU_ACCEL='-accel tcg'
-        ;;
-esac
-
 event "checking util-linux fdisk"
-if [ "$FDISK_BIN" = "" -a "$CROSS_COMPILE" = "" ]; then
+if [ "$FDISK" = "" ]; then
     if ! which fdisk > /dev/null 2>&1; then
         echo "no"
         exit 1
     fi
-    FDISK_BIN=`which fdisk`
+    FDISK=`which fdisk`
 fi
 
-if ! $FDISK_BIN -v 2>&1 | grep util-linux > /dev/null 2>&1 ; then
+if ! $FDISK -v 2>&1 | grep util-linux > /dev/null 2>&1 ; then
     echo "no, fdisk is not from util-linux"
     exit 1
 else
-    echo "$FDISK_BIN"
-fi
-
-event "checking cross compiling"
-if [ "$CROSS_COMPILE" != "" ]; then
-    echo "yes"
-    CROSS_COMPILE_FLAG='--toolchain cross-compile.cmake'
-    generate_cross_compile_script "$OS" "$CROSS_COMPILE" "$FDISK_BIN"
-else
-    echo "no"
-    CROSS_COMPILE_FLAG=
+    echo "$FDISK"
 fi
 
 event "checking mkfs tool"
@@ -136,9 +86,8 @@ else
 fi
 
 cp Makefile.src Makefile
-sed -i '' -e "s|##PLACEHOLDER_1##|$QEMU|" Makefile > /dev/null 2>&1
-sed -i '' -e "s|##PLACEHOLDER_2##|$GDB|" Makefile > /dev/null 2>&1
-sed -i '' -e "s|##PLACEHOLDER_3##|$QEMU_ACCEL|" Makefile > /dev/null 2>&1
-sed -i '' -e "s|##PLACEHOLDER_4##|$CROSS_COMPILE_FLAG|" Makefile > /dev/null 2>&1
-sed -i '' -e "s|##PLACEHOLDER_5##|$FDISK_BIN|" Makefile > /dev/null 2>&1
+sed -i '' -e "s|##DEFAULT_ARCH##|$DEFAULT_ARCH|" Makefile > /dev/null 2>&1
+sed -i '' -e "s|##GDB##|$GDB|" Makefile > /dev/null 2>&1
+sed -i '' -e "s|##QEMU##|$QEMU|" Makefile > /dev/null 2>&1
+sed -i '' -e "s|##FDISK##|$FDISK|" Makefile > /dev/null 2>&1
 exit 0

+ 25 - 2
crates/buddy_allocator/src/zone.rs

@@ -30,7 +30,18 @@ where
             if current_order > order {
                 self.expand(pages_ptr, current_order, order);
             }
-            assert!(pages_ptr.is_free() && pages_ptr.is_present());
+
+            assert!(
+                pages_ptr.is_present(),
+                "Page {:?} is not present",
+                pages_ptr.into(),
+            );
+
+            assert!(
+                pages_ptr.is_free(),
+                "Page {:?} is not free",
+                pages_ptr.into(),
+            );
 
             return Some(pages_ptr);
         }
@@ -57,6 +68,18 @@ where
         let mut pfn = Into::<PFN>::into(pages_ptr);
         let mut current_order = pages_ptr.order();
 
+        assert!(
+            pages_ptr.is_present(),
+            "Freeing a page that is not present: {:?}",
+            pages_ptr.into(),
+        );
+
+        assert!(
+            !pages_ptr.is_free(),
+            "Freeing a page that is free: {:?}",
+            pages_ptr.into(),
+        );
+
         while current_order < (AREAS - 1) as u32 {
             let buddy_pfn = pfn.buddy_pfn(current_order);
             let buddy_pages_ptr = Raw::from(buddy_pfn);
@@ -111,7 +134,7 @@ where
                 .trailing_zeros()
                 .min((AREAS - 1) as u32);
 
-            while start_pfn + order as usize > end_pfn {
+            while start_pfn + (1 << order) as usize > end_pfn {
                 order -= 1;
             }
             let page_ptr = Raw::from(start_pfn);

+ 18 - 0
crates/eonix_hal/Cargo.toml

@@ -0,0 +1,18 @@
+[package]
+name = "eonix_hal"
+version = "0.1.0"
+edition = "2024"
+links = "eonix_hal"
+
+[dependencies]
+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" }
+eonix_preempt = { path = "../eonix_preempt" }
+
+acpi = "5.2.0"
+cfg-if = "1.0"

+ 49 - 0
crates/eonix_hal/build.rs

@@ -0,0 +1,49 @@
+use std::path::PathBuf;
+use std::{env, fs};
+
+fn read_dependent_script(script: &str) -> Result<String, Box<dyn std::error::Error>> {
+    let content = fs::read_to_string(script)?;
+    println!("cargo:rerun-if-changed={}", script);
+    Ok(content)
+}
+
+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");
+
+    let memory = read_dependent_script("src/arch/x86_64/memory.x")?;
+    let link = read_dependent_script("src/arch/x86_64/link.x")?;
+
+    *script = memory + script;
+    script.push_str(&link);
+
+    Ok(())
+}
+
+fn process_ldscript_arch(
+    script: &mut String,
+    arch: &str,
+) -> Result<(), Box<dyn std::error::Error>> {
+    match arch {
+        "x86_64" => {
+            process_ldscript_x86(script)?;
+        }
+        _ => panic!("Unsupported architecture: {}", arch),
+    }
+
+    Ok(())
+}
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let out_dir = PathBuf::from(env::var("OUT_DIR")?);
+    let out_script = out_dir.join("link.x");
+
+    let in_script = "src/link.x.in";
+    let mut script = read_dependent_script(in_script)?;
+
+    process_ldscript_arch(&mut script, &env::var("CARGO_CFG_TARGET_ARCH")?)?;
+
+    fs::write(out_script, script)?;
+    println!("cargo:rustc-link-search={}", out_dir.display());
+    Ok(())
+}

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

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

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

@@ -0,0 +1,165 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse, parse_macro_input, spanned::Spanned as _, FnArg, ItemFn};
+
+/// Define the default trap handler. The function should take exactly one argument
+/// of type `&mut TrapContext`.
+///
+/// # Usage
+/// ```no_run
+/// #[eonix_hal::default_trap_handler]
+/// fn interrupt_handler(ctx: &mut TrapContext) {
+///     println!("Trap {} received!", ctx.trap_no());
+///     // ...
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn default_trap_handler(attrs: TokenStream, item: TokenStream) -> TokenStream {
+    let item = parse_macro_input!(item as ItemFn);
+
+    if !attrs.is_empty() {
+        return parse::Error::new(
+            item.span(),
+            "`default_trap_handler` attribute does not take any arguments",
+        )
+        .into_compile_error()
+        .into();
+    }
+
+    if item.sig.inputs.len() > 1 {
+        return parse::Error::new(
+            item.span(),
+            "`default_trap_handler` only takes one argument",
+        )
+        .into_compile_error()
+        .into();
+    }
+
+    let attrs = &item.attrs;
+    let arg = item.sig.inputs.first().unwrap();
+    let block = &item.block;
+
+    quote! {
+        #(#attrs)*
+        #[no_mangle]
+        pub unsafe extern "C" fn _default_trap_handler(#arg) #block
+    }
+    .into()
+}
+
+/// Define the entry point. The function should have signature like
+///
+/// ```ignore
+/// [unsafe] fn ident(ident: eonix_hal::bootstrap::BootStrapData) -> !
+/// ```
+///
+/// # Usage
+/// ```no_run
+/// #[eonix_hal::main]
+/// fn kernel_main(data: eonix_hal::bootstrap::BootStrapData) -> ! {
+///     // ...
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn main(attrs: TokenStream, item: TokenStream) -> TokenStream {
+    let item = parse_macro_input!(item as ItemFn);
+
+    if !attrs.is_empty() {
+        return parse::Error::new(item.span(), "`main` attribute does not take any arguments")
+            .into_compile_error()
+            .into();
+    }
+
+    if item.sig.inputs.len() != 1 {
+        return parse::Error::new(item.span(), "`main` should have exactly one argument.")
+            .into_compile_error()
+            .into();
+    }
+
+    let arg_ident = match item.sig.inputs.first().unwrap() {
+        FnArg::Receiver(_) => {
+            return parse::Error::new(
+                item.span(),
+                "`main` function cannot take `self` as an argument",
+            )
+            .into_compile_error()
+            .into();
+        }
+        FnArg::Typed(ty) => &ty.pat,
+    };
+
+    let ident = &item.sig.ident;
+    let attrs = item.attrs;
+    let unsafety = item.sig.unsafety;
+    let block = &item.block;
+
+    quote! {
+        #(#attrs)*
+        #[export_name = "_eonix_hal_main"]
+        pub #unsafety fn #ident(
+            #arg_ident: eonix_hal::bootstrap::BootStrapData,
+        ) -> ! #block
+    }
+    .into()
+}
+
+/// Define the AP entry point. The function should have signature like
+///
+/// ```ignore
+/// [unsafe] fn ident(ident: eonix_mm::address::PRange) -> !
+/// ```
+///
+/// # Usage
+/// ```no_run
+/// #[eonix_hal::main]
+/// fn ap_main(stack_range: eonix_mm::address::PRange) -> ! {
+///     // ...
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn ap_main(attrs: TokenStream, item: TokenStream) -> TokenStream {
+    let item = parse_macro_input!(item as ItemFn);
+
+    if !attrs.is_empty() {
+        return parse::Error::new(
+            item.span(),
+            "`ap_main` attribute does not take any arguments",
+        )
+        .into_compile_error()
+        .into();
+    }
+
+    if item.sig.inputs.len() != 1 {
+        return parse::Error::new(item.span(), "`ap_main` should have exactly one argument.")
+            .into_compile_error()
+            .into();
+    }
+
+    let arg_ident = match item.sig.inputs.first().unwrap() {
+        FnArg::Receiver(_) => {
+            return parse::Error::new(
+                item.span(),
+                "`ap_main` function cannot take `self` as an argument",
+            )
+            .into_compile_error()
+            .into();
+        }
+        FnArg::Typed(ty) => &ty.pat,
+    };
+
+    let ident = &item.sig.ident;
+    let attrs = item.attrs;
+    let unsafety = item.sig.unsafety;
+    let block = &item.block;
+
+    quote! {
+        #(#attrs)*
+        #[export_name = "_eonix_hal_ap_main"]
+        pub #unsafety fn #ident(
+            #arg_ident: eonix_mm::address::PRange,
+        ) -> ! #block
+    }
+    .into()
+}

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

@@ -0,0 +1,8 @@
+[package]
+name = "eonix_hal_traits"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+eonix_mm = { path = "../../eonix_mm" }
+bitflags = "2.6.0"

+ 41 - 0
crates/eonix_hal/eonix_hal_traits/src/context.rs

@@ -0,0 +1,41 @@
+#[doc(notable_trait)]
+pub trait RawTaskContext: Sized {
+    /// Creates a new instance of the task context with interrupt enabled and the program
+    /// counter and stack pointer set to zero (a.k.a. some invalid state).
+    ///
+    /// Using the created context without setting the program counter and stack pointer
+    /// will result in undefined behavior.
+    fn new() -> Self;
+
+    fn set_program_counter(&mut self, pc: usize);
+    fn set_stack_pointer(&mut self, sp: usize);
+
+    fn is_interrupt_enabled(&self) -> bool;
+    fn set_interrupt_enabled(&mut self, is_enabled: bool);
+
+    /// Sets the instruction pointer to the given function and prepares the context
+    /// to call it with the given argument.
+    fn call(&mut self, func: unsafe extern "C" fn(usize) -> !, arg: usize);
+
+    /// Switch the execution context from `from` to `to`.
+    ///
+    /// # Safety
+    /// This function is unsafe because it performs a context switch, which can lead to
+    /// undefined behavior if the contexts are not properly set up.
+    unsafe extern "C" fn switch(from: &mut Self, to: &mut Self);
+
+    /// Switches the execution context to `to` where we will not return.
+    ///
+    /// # Safety
+    /// This function is unsafe because it performs a context switch that does not return.
+    /// The caller must ensure that the `to` context is properly set up and that it will not
+    /// return to the caller.
+    unsafe extern "C" fn switch_to_noreturn(to: &mut Self) -> ! {
+        let mut from_ctx = Self::new();
+        unsafe {
+            Self::switch(&mut from_ctx, to);
+        }
+
+        unreachable!("We should never return from `switch_to_noreturn()`");
+    }
+}

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

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

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

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

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

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

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

@@ -0,0 +1,6 @@
+use eonix_mm::address::PRange;
+
+pub trait Memory {
+    fn present_ram() -> impl Iterator<Item = PRange>;
+    fn free_ram() -> impl Iterator<Item = PRange>;
+}

+ 7 - 0
crates/eonix_hal/eonix_hal_traits/src/processor.rs

@@ -0,0 +1,7 @@
+use core::{ops::Deref, pin::Pin};
+
+pub trait Processor {
+    fn local() -> impl Deref<Target = Pin<&'static mut Self>>
+    where
+        Self: 'static;
+}

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

@@ -0,0 +1,95 @@
+use crate::fault::Fault;
+use core::marker::PhantomData;
+
+/// A raw trap context.
+///
+/// This should be implemented by the architecture-specific trap context
+/// and will be used in the HAL crates.
+#[doc(notable_trait)]
+pub trait RawTrapContext: Copy {
+    fn new() -> Self;
+
+    fn trap_type(&self) -> TrapType;
+
+    fn get_program_counter(&self) -> usize;
+    fn get_stack_pointer(&self) -> usize;
+
+    fn set_program_counter(&mut self, pc: usize);
+    fn set_stack_pointer(&mut self, sp: usize);
+
+    fn is_interrupt_enabled(&self) -> bool;
+    fn set_interrupt_enabled(&mut self, enabled: bool);
+
+    fn is_user_mode(&self) -> bool;
+    fn set_user_mode(&mut self, user: bool);
+
+    fn set_user_return_value(&mut self, retval: usize);
+}
+
+#[doc(notable_trait)]
+pub trait TrapReturn {
+    /// Return to the context before the trap occurred.
+    ///
+    /// # Safety
+    /// This function is unsafe because the caller MUST ensure that the
+    /// context before the trap is valid, that is, that the stack pointer
+    /// points to a valid stack frame and the program counter points to some
+    /// valid instruction.
+    unsafe fn trap_return(&mut self);
+}
+
+pub trait IrqState {
+    /// Restore the IRQ state.
+    fn restore(self);
+}
+
+/// The reason that caused the trap.
+pub enum TrapType {
+    Syscall { no: usize, args: [usize; 6] },
+    Fault(Fault),
+    Irq(usize),
+    Timer,
+}
+
+/// A marker type that indicates that the type is a raw trap context.
+///
+/// # Usage
+///
+/// Check whether a type implements `RawTrapContext` using a `PhantomData` field.
+///
+/// The following code should fail to compile:
+///
+/// ```compile_fail
+/// # use eonix_hal_traits::trap::IsRawTrapContext;
+/// struct NonRawTrapContext; // Does not implement `RawTrapContext`!
+///
+/// // Compile-time error: `NonRawTrapContext` does not implement `RawTrapContext`.
+/// struct UserStruct(NonRawTrapContext, IsRawTrapContext<NonRawTrapContext>);
+/// ```
+///
+/// While the following code should compile:
+///
+/// ```no_run
+/// # use eonix_hal_traits::trap::IsRawTrapContext;
+/// struct RawTrapContextType;
+///
+/// impl RawTrapContext for RawTrapContextType {
+///     // ...
+/// #   fn new() -> Self { todo!() }
+/// #   fn trap_type() -> TrapType { todo!() }
+/// #   fn get_program_counter(&self) -> usize { todo!() }
+/// #   fn get_stack_pointer(&self) -> usize { todo!() }
+/// #   fn set_program_counter(&mut self, _: usize) { todo!() }
+/// #   fn set_stack_pointer(&mut self, _: usize) { todo!() }
+/// #   fn is_interrupt_enabled(&self) -> bool { todo!() }
+/// #   fn set_interrupt_enabled(&mut self, _: bool) { todo!() }
+/// #   fn is_user_mode(&self) -> bool { todo!() }
+/// #   fn set_user_mode(&mut self, _: bool) { todo!() }
+/// #   fn set_user_return_value(&mut self, _: usize) { todo!() }
+/// }
+///
+/// struct UserStruct(RawTrapContextType, IsRawTrapContext<RawTrapContextType>);
+/// ```
+pub struct IsRawTrapContext<T>(PhantomData<T>)
+where
+    T: RawTrapContext;

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

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

+ 516 - 0
crates/eonix_hal/src/arch/x86_64/bootstrap.rs

@@ -0,0 +1,516 @@
+pub(crate) mod init;
+
+use super::mm::{E820_MEM_MAP_DATA, PA_G, PA_NXE, PA_P, PA_PS, PA_RW};
+use core::arch::{global_asm, naked_asm};
+
+const KERNEL_IMAGE_PADDR: usize = 0x200000;
+const KERNEL_PML4: usize = 0x1000;
+const KERNEL_PDPT_PHYS_MAPPING: usize = 0x2000;
+const KERNEL_PDPT_KERNEL_SPACE: usize = 0x3000;
+const KERNEL_PD_KIMAGE: usize = 0x4000;
+const KERNEL_PT_KIMAGE: usize = 0x5000;
+
+#[unsafe(link_section = ".low")]
+static mut EARLY_GDT: [u64; 7] = [0; 7];
+
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".low")]
+static mut EARLY_GDT_DESCRIPTOR: (u16, u32) = (0, 0);
+
+#[unsafe(link_section = ".low")]
+static mut BIOS_IDT_DESCRIPTOR: (u16, u32) = (0, 0);
+
+unsafe extern "C" {
+    fn KIMAGE_32K_COUNT();
+    fn KIMAGE_PAGES();
+
+    fn STAGE1_MAGIC();
+    fn STAGE1_MAGIC_VALUE();
+
+    fn start_32bit() -> !;
+}
+
+global_asm!(
+    r#"
+    .pushsection .mbr, "ax", @progbits
+    .code16
+
+    .globl move_mbr
+    move_mbr:
+        xor %ax, %ax
+        mov %ax, %ds
+        mov %ax, %es
+        mov %ax, %ss
+
+        # move the MBR to 0xe00
+        mov $128, %cx # 512 bytes
+        mov $0x7c00, %si
+        mov $0x0e00, %di
+        rep movsl
+
+        ljmp $0x00, $2f
+
+    2:
+        # read the kernel stage1
+        mov $.Lread_data_packet, %si
+        mov $0x42, %ah
+        mov $0x80, %dl
+        int $0x13
+        jc .Lhalt16
+
+        # get memory size info and storage it
+        mov $0xe801, %ax
+        int $0x15
+        jc .Lhalt16
+
+        cmp $0x86, %ah # unsupported function
+        je .Lhalt16
+        cmp $0x80, %ah # invalid command
+        je .Lhalt16
+
+        jcxz 2f
+        mov %cx, %ax
+        mov %dx, %bx
+
+    2:
+        mov ${e820_data_addr}, %esp
+        movzw %ax, %eax
+        mov %eax, 8(%esp)  # 1k blocks
+        movzw %bx, %ebx
+        mov %ebx, 12(%esp) # 64k blocks
+
+        # save the destination address to es:di
+        mov %sp, %di
+        add $16, %di # buffer is 1024 - 16 bytes
+
+        # set default entry size
+        movl $20, 4(%esp)
+
+        # clear %ebx, len
+        xor %ebx, %ebx
+        mov %ebx, (%esp)
+
+    2:
+        # set the magic number to edx
+        mov $0x534D4150, %edx
+
+        # set function number to eax
+        mov $0xe820, %eax
+
+        # set default entry size
+        mov $24, %ecx
+
+        int $0x15
+
+        incl (%esp)
+        add $24, %edi
+
+        jc .Lsave_mem_fin
+        cmp $0, %ebx
+        jz .Lsave_mem_fin
+
+        cmp $24, %ecx
+        cmovnz 4(%esp), %ecx
+        mov %ecx, 4(%esp)
+
+        jmp 2b
+
+    .Lsave_mem_fin:
+        mov $0x3ff, %ax
+        mov ${bios_idt_descriptor}, %di
+        mov %ax, (%di)
+
+        xor %eax, %eax
+        mov %eax, 2(%di)
+
+        lgdt .Learly_gdt_descriptor
+
+        cli
+        # IDT descriptor is 6 0's. borrow the null gdt entry
+        lidt .Learly_gdt
+
+        # enable protection mode
+        mov %cr0, %eax
+        or $1, %eax
+        mov %eax, %cr0
+
+        ljmp $0x08, ${start_32bit}
+
+    .Lhalt16:
+        hlt
+        jmp .
+
+    .align 16
+    .Learly_gdt:
+        .8byte 0x0                # null selector
+        .8byte 0x00cf9a000000ffff # 32bit code selector
+        .8byte 0x00cf92000000ffff # 32bit data selector
+
+    .align 4
+    .Learly_gdt_descriptor:
+        .word 0x17 # size
+        .long .Learly_gdt  # address
+
+    .align 16
+    .Lread_data_packet:
+        .long  0x00070010 # .stage1 takes up 3.5K, or 7 sectors
+        .long  0x00006000 # read to 0000:6000
+        .8byte 1          # read from LBA 1
+    .popsection
+    "#,
+    start_32bit = sym start_32bit,
+    bios_idt_descriptor = sym BIOS_IDT_DESCRIPTOR,
+    e820_data_addr = sym E820_MEM_MAP_DATA,
+    options(att_syntax),
+);
+
+global_asm!(
+    r#"
+    .pushsection .stage1, "ax", @progbits
+    .code16
+    .Lhalt:
+        hlt
+        jmp .
+    
+    # scratch %eax
+    # return address should be of 2 bytes, and will be zero extended to 4 bytes
+    .Lgo_32bit:
+        cli
+        # borrow the null entry from the early gdt
+        lidt {EARLY_GDT}
+
+        # set PE bit
+        mov %cr0, %eax
+        or $1, %eax
+        mov %eax, %cr0
+
+        ljmp $0x18, $.Lgo_32bit0
+
+    .Lgo_16bit0:
+        mov $0x30, %ax
+        mov %ax, %ds
+        mov %ax, %es
+        mov %ax, %ss
+
+        lidt {BIOS_IDT_DESCRIPTOR}
+
+        mov %cr0, %eax
+        and $0xfffffffe, %eax
+        mov %eax, %cr0
+
+        ljmp $0x00, $2f
+
+    2:
+        xor %ax, %ax
+        mov %ax, %ds
+        mov %ax, %ss
+        mov %ax, %es
+
+        sti
+
+        pop %eax
+        push %ax
+        ret
+
+    .code32
+    # scratch %eax
+    # return address should be of 4 bytes, and extra 2 bytes will be popped from the stack
+    .Lgo_16bit:
+        cli
+        ljmp $0x28, $.Lgo_16bit0
+
+    .Lgo_32bit0:
+        mov $0x20, %ax
+        mov %ax, %ds
+        mov %ax, %es
+        mov %ax, %ss
+
+        pop %ax
+        movzw %ax, %eax
+        push %eax
+        ret
+
+    # build read disk packet on the stack and perform read operation
+    #
+    # read 16k to 0x8000 and then copy to destination
+    #
+    # %edi: lba start
+    # %esi: destination
+    .code32
+    read_disk:
+        push %ebp
+        mov %esp, %ebp
+
+        lea -24(%esp), %esp
+
+        mov $0x00200010, %eax # packet size 0, sector count 64
+        mov %eax, (%esp)
+
+        mov $0x08000000, %eax # destination address 0x0800:0x0000
+        mov %eax, 4(%esp)
+
+        mov %edi, 8(%esp)  # lba low 4bytes
+
+        xor %eax, %eax
+        mov %eax, 12(%esp) # lba high 2bytes
+
+        mov %esi, %edi
+        mov %esp, %esi # packet address
+
+        call .Lgo_16bit
+    .code16
+        mov $0x42, %ah
+        mov $0x80, %dl
+        int $0x13
+        jc .Lhalt
+
+        call .Lgo_32bit
+    .code32
+        # move data to destination
+        mov $0x8000, %esi
+        mov $4096, %ecx
+        rep movsl
+
+        mov %ebp, %esp
+        pop %ebp
+        ret
+
+    .align 8
+    .Lgdt_data:
+        .8byte 0x00209a0000000000 # 64bit code selector
+        .8byte 0x0000920000000000 # 64bit data selector
+        .8byte 0x00cf9a000000ffff # 32bit code selector
+        .8byte 0x00cf92000000ffff # 32bit data selector
+        .8byte 0x000f9a000000ffff # 16bit code selector
+        .8byte 0x000f92000000ffff # 16bit data selector
+
+    {start_32bit}:
+        mov $0x10, %ax
+        mov %ax, %ds
+        mov %ax, %es
+        mov %ax, %ss
+
+        mov ${STAGE1_MAGIC}, %edi
+        mov (%edi), %edi
+
+        cmp ${STAGE1_MAGIC_VALUE}, %edi
+        jne .Lhalt
+
+        mov ${EARLY_GDT_DESCRIPTOR}, %edi
+        mov $0x37, %ax
+        mov %ax, (%edi)
+
+        mov ${EARLY_GDT}, %eax
+        mov %eax, 2(%edi)
+
+        # fill in early kernel GDT
+        xchg %eax, %edi
+        xor %eax, %eax
+        mov $2, %ecx
+
+        # null segment
+        rep stosl
+
+        # other data
+        mov $.Lgdt_data, %esi
+        mov $12, %ecx
+
+        rep movsl
+
+        lgdt {EARLY_GDT_DESCRIPTOR}
+        ljmp $0x18, $2f
+
+    2:
+        mov $0x20, %ax
+        mov %ax, %ds
+        mov %ax, %es
+        mov %ax, %ss
+
+        # temporary kernel stack
+        mov $0x1000, %esp
+
+        # read kimage into memory
+        lea -16(%esp), %esp
+        mov ${KIMAGE_32K_COUNT}, %ecx
+        shl $1, %ecx
+        movl ${KERNEL_IMAGE_PADDR}, 4(%esp) # destination address
+        movl $8, (%esp) # LBA
+
+    2:
+        mov (%esp), %edi
+        mov 4(%esp), %esi
+
+        mov %ecx, %ebx
+        call read_disk
+        mov %ebx, %ecx
+
+        addl $0x4000, 4(%esp)
+        addl $32, (%esp)
+
+        loop 2b
+
+        lea 16(%esp), %esp
+
+        cld
+        xor %eax, %eax
+
+        # clear paging structures
+        mov $0x1000, %edi
+        mov $0x5000, %ecx
+        shr $2, %ecx # %ecx /= 4
+        rep stosl
+
+        # set P, RW, G
+        mov $({PA_P} | {PA_RW} | {PA_G}), %ebx
+        xor %edx, %edx
+        mov ${KERNEL_PDPT_PHYS_MAPPING}, %esi
+
+        # PML4E 0x000
+        # we need the first 1GB identically mapped
+        # so that we won't trigger a triple fault after
+        # enabling paging
+        mov ${KERNEL_PML4}, %edi
+        call fill_pxe
+
+        # PML4E 0xff0
+        mov $({PA_NXE} >> 32), %edx
+        lea 0xff0(%edi), %edi
+        call fill_pxe
+        xor %edx, %edx
+
+        # setup PDPT for physical memory mapping
+        mov ${KERNEL_PDPT_PHYS_MAPPING}, %edi
+
+        # set PS
+        or ${PA_PS}, %ebx
+        mov $512, %ecx
+        xor %esi, %esi
+    2:
+        call fill_pxe
+        lea 8(%edi), %edi
+        add $0x40000000, %esi # 1GB
+        adc $0, %edx
+        loop 2b
+
+        xor %edx, %edx
+
+        # PML4E 0xff8
+        mov ${KERNEL_PDPT_KERNEL_SPACE}, %esi
+        mov ${KERNEL_PML4}, %edi
+        lea 0xff8(%edi), %edi
+        # clear PS
+        and $(~{PA_PS}), %ebx
+        call fill_pxe
+
+        # PDPTE 0xff8
+        mov ${KERNEL_PDPT_KERNEL_SPACE}, %edi
+        lea 0xff8(%edi), %edi
+        mov ${KERNEL_PD_KIMAGE}, %esi
+        call fill_pxe
+
+        # PDE 0xff0
+        mov ${KERNEL_PD_KIMAGE}, %edi
+        lea 0xff0(%edi), %edi
+        mov ${KERNEL_PT_KIMAGE}, %esi # 0x104000
+        call fill_pxe
+
+        # fill PT (kernel image)
+        mov ${KERNEL_PT_KIMAGE}, %edi
+        mov ${KERNEL_IMAGE_PADDR}, %esi
+
+        mov ${KIMAGE_PAGES}, %ecx
+
+    2:
+        call fill_pxe
+        lea 8(%edi), %edi
+        lea 0x1000(%esi), %esi
+        loop 2b
+
+        # 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
+
+    # %ebx: attribute low
+    # %edx: attribute high
+    # %esi: page physical address
+    # %edi: page x entry address
+    fill_pxe:
+        lea (%ebx, %esi, 1), %eax
+        mov %eax, (%edi)
+        mov %edx, 4(%edi)
+
+        ret
+
+    .code64
+    2:
+        jmp {start_64bit}
+    
+    .popsection
+    "#,
+    EARLY_GDT = sym EARLY_GDT,
+    EARLY_GDT_DESCRIPTOR = sym EARLY_GDT_DESCRIPTOR,
+    BIOS_IDT_DESCRIPTOR = sym BIOS_IDT_DESCRIPTOR,
+    KIMAGE_32K_COUNT = sym KIMAGE_32K_COUNT,
+    KIMAGE_PAGES = sym KIMAGE_PAGES,
+    STAGE1_MAGIC = sym STAGE1_MAGIC,
+    STAGE1_MAGIC_VALUE = sym STAGE1_MAGIC_VALUE,
+    KERNEL_IMAGE_PADDR = const KERNEL_IMAGE_PADDR,
+    KERNEL_PML4 = const KERNEL_PML4,
+    PA_P = const PA_P,
+    PA_RW = const PA_RW,
+    PA_G = const PA_G,
+    PA_PS = const PA_PS,
+    PA_NXE = const PA_NXE,
+    KERNEL_PDPT_PHYS_MAPPING = const KERNEL_PDPT_PHYS_MAPPING,
+    KERNEL_PDPT_KERNEL_SPACE = const KERNEL_PDPT_KERNEL_SPACE,
+    KERNEL_PD_KIMAGE = const KERNEL_PD_KIMAGE,
+    KERNEL_PT_KIMAGE = const KERNEL_PT_KIMAGE,
+    start_64bit = sym start_64bit,
+    start_32bit = sym start_32bit,
+    options(att_syntax),
+);
+
+#[unsafe(naked)]
+pub unsafe extern "C" fn start_64bit() {
+    naked_asm!(
+        "mov $0x10, %ax",
+        "mov %ax, %ds",
+        "mov %ax, %es",
+        "mov %ax, %ss",
+        "",
+        "mov ${kernel_identical_base}, %rax",
+        "mov ${stack_paddr}, %rsp",
+        "add %rax, %rsp",
+        "",
+        "xor %rbp, %rbp", // Clear previous stack frame
+        "push %rbp", // NULL return address
+        "",
+        "mov ${e820_data_addr}, %rdi",
+        "add %rax, %rdi",
+        "",
+        "jmp {kernel_init}",
+        kernel_identical_base = const 0xffffff0000000000u64,
+        stack_paddr = const 0x80000,
+        e820_data_addr = sym E820_MEM_MAP_DATA,
+        kernel_init = sym init::kernel_init,
+        options(att_syntax)
+    )
+}

+ 374 - 0
crates/eonix_hal/src/arch/x86_64/bootstrap/init.rs

@@ -0,0 +1,374 @@
+use crate::{
+    arch::{
+        bootstrap::{EARLY_GDT_DESCRIPTOR, KERNEL_PML4},
+        cpu::CPU,
+        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},
+    cell::RefCell,
+    hint::spin_loop,
+    sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering},
+};
+use eonix_hal_traits::mm::Memory;
+use eonix_mm::{
+    address::{Addr as _, PAddr, PRange, PhysAccess, VRange},
+    page_table::{PageAttribute, PagingMode, PTE as _},
+    paging::{Page, PageAccess, PageAlloc, PAGE_SIZE},
+};
+use eonix_percpu::PercpuArea;
+
+static BSP_PAGE_ALLOC: AtomicPtr<RefCell<BasicPageAlloc>> = AtomicPtr::new(core::ptr::null_mut());
+
+static AP_COUNT: AtomicUsize = AtomicUsize::new(0);
+static AP_STACK: AtomicUsize = AtomicUsize::new(0);
+static AP_SEM: AtomicBool = AtomicBool::new(false);
+
+global_asm!(
+    r#"
+    .pushsection .stage1.smp, "ax", @progbits
+    .code16
+    ljmp $0x0, $2f
+
+    2:
+    lgdt {early_gdt_descriptor}
+    mov $0xc0000080, %ecx
+    rdmsr
+    or $0x901, %eax # set LME, NXE, SCE
+    wrmsr
+
+    mov %cr4, %eax
+    or $0xa0, %eax # set PAE, PGE
+    mov %eax, %cr4
+
+    mov ${kernel_pml4}, %eax
+    mov %eax, %cr3
+
+    mov %cr0, %eax
+    or $0x80010001, %eax # set PE, WP, PG
+    mov %eax, %cr0
+
+    ljmp $0x08, $2f
+
+    .code64
+    2:
+    mov $0x10, %ax
+    mov %ax, %ds
+    mov %ax, %es
+    mov %ax, %ss
+
+    xor %rax, %rax
+    inc %rax
+    mov ${ap_semaphore}, %rcx
+
+    2:
+    xchg %rax, (%rcx) # AcqRel
+    cmp $0, %rax
+    je 2f
+    pause
+    jmp 2b
+
+    2:
+    mov ${ap_stack}, %rcx
+
+    2:
+    mov (%rcx), %rsp # Acquire
+    cmp $0, %rsp
+    jne 2f
+    pause
+    jmp 2b
+
+    2:
+    xor %rbp, %rbp
+    mov %rbp, (%rcx) # Relaxed
+
+    mov ${ap_semaphore}, %rcx
+    xchg %rax, (%rcx) # Release
+
+    mov %rsp, %rdi
+    push %rbp # NULL return address
+    mov ${ap_entry}, %rax
+    jmp *%rax
+
+    .popsection
+    "#,
+    early_gdt_descriptor = sym EARLY_GDT_DESCRIPTOR,
+    kernel_pml4 = const KERNEL_PML4,
+    ap_semaphore = sym AP_SEM,
+    ap_stack = sym AP_STACK,
+    ap_entry = sym ap_entry,
+    options(att_syntax),
+);
+
+fn enable_sse() {
+    unsafe {
+        asm!(
+            "mov %cr0, %rax",
+            "and $(~0xc), %rax",
+            "or $0x22, %rax",
+            "mov %rax, %cr0",
+            "mov %cr4, %rax",
+            "or $0x600, %rax",
+            "mov %rax, %cr4",
+            "fninit",
+            out("rax") _,
+            options(att_syntax, nomem, nostack)
+        )
+    }
+}
+
+fn setup_cpu(alloc: impl PageAlloc) {
+    let mut percpu_area = PercpuArea::new(|layout| {
+        // TODO: Use page size defined in `arch`.
+        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
+    });
+
+    percpu_area.setup(|pointer| {
+        wrmsr(0xC0000101, pointer.addr().get() as u64);
+
+        unsafe {
+            // SAFETY: %gs:0 points to the start of the percpu area.
+            asm!(
+                "movq {}, %gs:0",
+                in(reg) pointer.addr().get(),
+                options(nostack, preserves_flags, att_syntax)
+            );
+        }
+    });
+
+    let mut cpu = CPU::local();
+    unsafe {
+        // SAFETY: Preemption is disabled and interrupt MUST be disabled since
+        //         we are doing this in the kernel initialization phase.
+        cpu.as_mut().init();
+    }
+
+    percpu_area.register(cpu.cpuid());
+}
+
+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);
+    const PIC2_COMMAND: Port8 = Port8::new(0xA0);
+    const PIC2_DATA: Port8 = Port8::new(0xA1);
+
+    // Initialize PIC
+    PIC1_COMMAND.write(0x11); // edge trigger mode
+    PIC1_DATA.write(0x20); // IRQ 0-7 offset
+    PIC1_DATA.write(0x04); // cascade with slave PIC
+    PIC1_DATA.write(0x01); // no buffer mode
+
+    PIC2_COMMAND.write(0x11); // edge trigger mode
+    PIC2_DATA.write(0x28); // IRQ 8-15 offset
+    PIC2_DATA.write(0x02); // cascade with master PIC
+    PIC2_DATA.write(0x01); // no buffer mode
+
+    // Allow all IRQs
+    PIC1_DATA.write(0x0);
+    PIC2_DATA.write(0x0);
+}
+
+fn bootstrap_smp(alloc: impl Allocator, page_alloc: &RefCell<BasicPageAlloc>) {
+    #[derive(Clone)]
+    struct Handler;
+
+    impl AcpiHandler for Handler {
+        unsafe fn map_physical_region<T>(
+            &self,
+            physical_address: usize,
+            size: usize,
+        ) -> PhysicalMapping<Self, T> {
+            unsafe {
+                PhysicalMapping::new(
+                    physical_address,
+                    ArchPhysAccess::as_ptr(PAddr::from(physical_address)),
+                    size,
+                    size,
+                    self.clone(),
+                )
+            }
+        }
+
+        fn unmap_physical_region<T>(_: &PhysicalMapping<Self, T>) {}
+    }
+
+    let acpi_tables = unsafe {
+        // SAFETY: Probing for RSDP in BIOS memory should be fine.
+        AcpiTables::search_for_rsdp_bios(Handler).unwrap()
+    };
+
+    let platform_info = PlatformInfo::new_in(&acpi_tables, &alloc).unwrap();
+    let processor_info = platform_info.processor_info.unwrap();
+
+    let ap_count = processor_info
+        .application_processors
+        .iter()
+        .filter(|ap| !matches!(ap.state, ProcessorState::Disabled))
+        .count();
+
+    unsafe {
+        CPU::local().bootstrap_cpus();
+    }
+
+    for current_count in 0..ap_count {
+        let stack_range = {
+            let page_alloc = BasicPageAllocRef::new(&page_alloc);
+
+            let ap_stack = Page::alloc_order_in(3, page_alloc);
+            let stack_range = ap_stack.range();
+            ap_stack.into_raw();
+
+            stack_range
+        };
+
+        // SAFETY: All the APs can see the allocator work done before this point.
+        let old = BSP_PAGE_ALLOC.swap((&raw const *page_alloc) as *mut _, Ordering::Release);
+        assert!(
+            old.is_null(),
+            "BSP_PAGE_ALLOC should be null before we release it"
+        );
+
+        // SAFETY: The AP reading the stack will see the allocation work.
+        while let Err(_) = AP_STACK.compare_exchange_weak(
+            0,
+            stack_range.end().addr(),
+            Ordering::Release,
+            Ordering::Relaxed,
+        ) {
+            // Spin until we can set the stack pointer for the AP.
+            spin_loop();
+        }
+
+        spin_loop();
+
+        // SAFETY: Make sure if we read the AP count, the allocator MUST have been released.
+        while AP_COUNT.load(Ordering::Acquire) == current_count {
+            // Wait for the AP to finish its initialization.
+            spin_loop();
+        }
+
+        // SAFETY: We acquire the work done by the AP.
+        let old = BSP_PAGE_ALLOC.swap(core::ptr::null_mut(), Ordering::Acquire);
+        assert_eq!(
+            old as *const _, &raw const *page_alloc,
+            "We should read the previously saved allocator"
+        );
+    }
+}
+
+pub extern "C" fn kernel_init() -> ! {
+    let global_page_table = &GLOBAL_PAGE_TABLE;
+    let paging_levels = ArchPagingMode::LEVELS;
+
+    enable_sse();
+
+    let real_allocator = RefCell::new(BasicPageAlloc::new());
+    let alloc = BasicPageAllocRef::new(&real_allocator);
+
+    unsafe extern "C" {
+        fn BSS_LENGTH();
+    }
+
+    for range in ArchMemory::free_ram() {
+        real_allocator.borrow_mut().add_range(range);
+    }
+
+    // Map kernel BSS
+    for pte in global_page_table.iter_kernel_in(
+        VRange::from(V_KERNEL_BSS_START).grow(BSS_LENGTH as usize),
+        paging_levels,
+        &alloc,
+    ) {
+        let attr = PageAttribute::PRESENT
+            | PageAttribute::WRITE
+            | PageAttribute::READ
+            | PageAttribute::HUGE
+            | PageAttribute::GLOBAL;
+
+        let page = Page::alloc_in(&alloc);
+        pte.set(page.into_raw(), attr.into());
+    }
+
+    unsafe {
+        // SAFETY: We've just mapped the area with sufficient length.
+        core::ptr::write_bytes(V_KERNEL_BSS_START.addr() as *mut (), 0, BSS_LENGTH as usize);
+    }
+
+    setup_cpu(&alloc);
+    setup_pic();
+
+    ScopedAllocator::new(&mut [0; 1024])
+        .with_alloc(|mem_alloc| bootstrap_smp(mem_alloc, &real_allocator));
+
+    unsafe extern "Rust" {
+        fn _eonix_hal_main(_: BootStrapData) -> !;
+    }
+
+    let bootstrap_data = BootStrapData {
+        early_stack: PRange::new(PAddr::from(0x6000), PAddr::from(0x80000)),
+        allocator: Some(real_allocator),
+    };
+
+    unsafe {
+        _eonix_hal_main(bootstrap_data);
+    }
+}
+
+pub extern "C" fn ap_entry(stack_bottom: PAddr) -> ! {
+    let stack_range = PRange::new(stack_bottom - (1 << 3) * PAGE_SIZE, stack_bottom);
+
+    {
+        // SAFETY: Acquire all the work done by the BSP and other APs.
+        let alloc = loop {
+            let alloc = BSP_PAGE_ALLOC.swap(core::ptr::null_mut(), Ordering::AcqRel);
+
+            if !alloc.is_null() {
+                break alloc;
+            }
+        };
+
+        let ref_alloc = unsafe { &*alloc };
+        setup_cpu(BasicPageAllocRef::new(&ref_alloc));
+
+        // SAFETY: Release our allocation work.
+        BSP_PAGE_ALLOC.store(alloc, Ordering::Release);
+    }
+
+    // SAFETY: Make sure the allocator is set before we increment the AP count.
+    AP_COUNT.fetch_add(1, Ordering::Release);
+
+    unsafe extern "Rust" {
+        fn _eonix_hal_ap_main(stack_range: PRange) -> !;
+    }
+
+    unsafe {
+        _eonix_hal_ap_main(stack_range);
+    }
+}

+ 46 - 77
arch/src/x86_64/context.rs → crates/eonix_hal/src/arch/x86_64/context.rs

@@ -1,6 +1,7 @@
 use core::arch::naked_asm;
+use eonix_hal_traits::context::RawTaskContext;
 
-/// Necessary hardware states of task for context switch
+/// Necessary hardware states of task for doing context switches.
 #[repr(C)]
 #[derive(Debug, Default)]
 pub struct TaskContext {
@@ -34,73 +35,8 @@ impl TaskContext {
         }
     }
 
-    pub fn ip(&mut self, ip: usize) {
-        self.rip = ip as u64;
-    }
-
-    pub fn sp(&mut self, sp: usize) {
-        self.rsp = sp as u64;
-    }
-
-    pub fn call1(&mut self, func: unsafe extern "C" fn(usize) -> !, arg: [usize; 1]) {
-        self.ip(Self::do_call as _);
-        self.rbp = func as _;
-        self.r12 = arg[0] as _;
-    }
-
-    pub fn call2(&mut self, func: unsafe extern "C" fn(usize, usize) -> !, arg: [usize; 2]) {
-        self.ip(Self::do_call as _);
-        self.rbp = func as _;
-
-        (self.r12, self.r13) = (arg[0] as _, arg[1] as _);
-    }
-
-    pub fn call3(&mut self, func: unsafe extern "C" fn(usize, usize, usize) -> !, arg: [usize; 3]) {
-        self.ip(Self::do_call as _);
-        self.rbp = func as _;
-
-        (self.r12, self.r13, self.r14) = (arg[0] as _, arg[1] as _, arg[2] as _);
-    }
-
-    pub fn call4(
-        &mut self,
-        func: unsafe extern "C" fn(usize, usize, usize, usize) -> !,
-        arg: [usize; 4],
-    ) {
-        self.ip(Self::do_call as _);
-        self.rbp = func as _;
-
-        (self.r12, self.r13, self.r14, self.r15) =
-            (arg[0] as _, arg[1] as _, arg[2] as _, arg[3] as _);
-    }
-
-    pub fn call5(
-        &mut self,
-        func: unsafe extern "C" fn(usize, usize, usize, usize, usize) -> !,
-        arg: [usize; 5],
-    ) {
-        self.ip(Self::do_call as _);
-        self.rbp = func as _;
-
-        (self.r12, self.r13, self.r14, self.r15, self.rbx) = (
-            arg[0] as _,
-            arg[1] as _,
-            arg[2] as _,
-            arg[3] as _,
-            arg[4] as _,
-        );
-    }
-
-    pub fn interrupt(&mut self, is_enabled: bool) {
-        if is_enabled {
-            self.rflags |= 0x200; // IF = 1
-        } else {
-            self.rflags &= !0x200; // IF = 0
-        }
-    }
-
-    #[naked]
-    pub unsafe extern "C" fn switch(from: &mut Self, to: &mut Self) {
+    #[unsafe(naked)]
+    unsafe extern "C" fn _switch(from: &mut Self, to: &mut Self) {
         naked_asm!(
             "pop %rax",
             "pushf",
@@ -132,19 +68,52 @@ impl TaskContext {
         );
     }
 
-    #[naked]
-    /// Maximum of 5 arguments supported.
+    #[unsafe(naked)]
     unsafe extern "C" fn do_call() -> ! {
         naked_asm!(
             "mov %r12, %rdi",
-            "mov %r13, %rsi",
-            "mov %r14, %rdx",
-            "mov %r15, %rcx",
-            "mov %rbx, %r8",
-            "mov %rbp, %rax",
-            "xor %rbp, %rbp",
-            "jmp *%rax",
+            "push %rbp", // NULL return address.
+            "jmp *%rbx",
             options(att_syntax),
         );
     }
 }
+
+impl RawTaskContext for TaskContext {
+    fn new() -> Self {
+        Self::new()
+    }
+
+    fn set_program_counter(&mut self, pc: usize) {
+        self.rip = pc as u64;
+    }
+
+    fn set_stack_pointer(&mut self, sp: usize) {
+        self.rsp = sp as u64;
+    }
+
+    fn is_interrupt_enabled(&self) -> bool {
+        (self.rflags & 0x200) != 0 // IF = 1
+    }
+
+    fn set_interrupt_enabled(&mut self, is_enabled: bool) {
+        if is_enabled {
+            self.rflags |= 0x200; // IF = 1
+        } else {
+            self.rflags &= !0x200; // IF = 0
+        }
+    }
+
+    fn call(&mut self, func: unsafe extern "C" fn(usize) -> !, arg: usize) {
+        self.set_program_counter(Self::do_call as _);
+        self.rbx = func as _;
+        self.r12 = arg as _;
+        self.rbp = 0; // NULL previous stack frame
+    }
+
+    unsafe extern "C" fn switch(from: &mut Self, to: &mut Self) {
+        unsafe {
+            Self::_switch(from, to);
+        }
+    }
+}

+ 132 - 60
arch/src/x86_64/init.rs → crates/eonix_hal/src/arch/x86_64/cpu.rs

@@ -1,5 +1,12 @@
-use super::{enable_sse, GDTEntry, InterruptControl, GDT};
-use core::{pin::Pin, ptr::addr_of};
+use super::gdt::{GDTEntry, GDT};
+use super::interrupt::InterruptControl;
+use core::marker::PhantomPinned;
+use core::pin::Pin;
+use eonix_preempt::PreemptGuard;
+use eonix_sync_base::LazyLock;
+
+#[eonix_percpu::define_percpu]
+static LOCAL_CPU: LazyLock<CPU> = LazyLock::new(CPU::new);
 
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
@@ -20,27 +27,18 @@ pub(crate) struct TSS {
     _reserved5: u32,
     _reserved6: u16,
     iomap_base: u16,
+    _pinned: PhantomPinned,
 }
 
-impl TSS {
-    pub fn new() -> Self {
-        Self {
-            _reserved1: 0,
-            rsp: [TSS_SP { low: 0, high: 0 }; 3],
-            _reserved2: 0,
-            _reserved3: 0,
-            ist: [TSS_SP { low: 0, high: 0 }; 7],
-            _reserved4: 0,
-            _reserved5: 0,
-            _reserved6: 0,
-            iomap_base: 0,
-        }
-    }
-
-    pub fn set_rsp0(&mut self, rsp: u64) {
-        self.rsp[0].low = rsp as u32;
-        self.rsp[0].high = (rsp >> 32) as u32;
-    }
+#[derive(Debug, Clone)]
+pub enum UserTLS {
+    /// TODO: This is not used yet.
+    #[allow(dead_code)]
+    TLS64(u64),
+    TLS32 {
+        base: u64,
+        desc: GDTEntry,
+    },
 }
 
 /// Architecture-specific cpu status data.
@@ -48,7 +46,23 @@ pub struct CPU {
     cpuid: usize,
     gdt: GDT,
     tss: TSS,
-    pub interrupt: InterruptControl,
+    interrupt: InterruptControl,
+}
+
+impl UserTLS {
+    /// # Return
+    /// Returns the TLS descriptor and the index of the TLS segment.
+    pub fn new32(base: u32, limit: u32, is_limit_in_pages: bool) -> (Self, u32) {
+        let flags = if is_limit_in_pages { 0xc } else { 0x4 };
+
+        (
+            Self::TLS32 {
+                base: base as u64,
+                desc: GDTEntry::new(base, limit, 0xf2, flags),
+            },
+            7,
+        )
+    }
 }
 
 impl CPU {
@@ -67,19 +81,29 @@ impl CPU {
     ///
     /// # Safety
     /// Make sure preemption and interrupt are disabled before calling this function.
-    pub unsafe fn init(self: Pin<&mut Self>) {
-        enable_sse();
-
-        // SAFETY: We don't move the object.
-        let self_mut = self.get_unchecked_mut();
+    pub(crate) unsafe fn init(mut self: Pin<&mut Self>) {
+        let tss = &self.as_ref().get_ref().tss;
+        let tss_addr = tss as *const _ as u64;
+
+        let mut gdt = unsafe {
+            // SAFETY: We don't move the field out.
+            self.as_mut().map_unchecked_mut(|me| &mut me.gdt)
+        };
+
+        unsafe {
+            // SAFETY: We don't move `gdt` out.
+            gdt.as_mut().get_unchecked_mut().set_tss(tss_addr as u64);
+        }
+        gdt.load();
 
-        let tss_addr = addr_of!(self_mut.tss);
-        self_mut.gdt.set_tss(tss_addr as u64);
-        self_mut.gdt.load();
+        let mut interrupt = unsafe {
+            // SAFETY: We don't move the field out.
+            self.as_mut().map_unchecked_mut(|me| &mut me.interrupt)
+        };
 
         // SAFETY: `self` is pinned, so are its fields.
-        Pin::new_unchecked(&mut self_mut.interrupt).setup_idt();
-        self_mut.interrupt.setup_timer();
+        interrupt.as_mut().setup_idt();
+        interrupt.as_mut().setup_timer();
     }
 
     /// Bootstrap all CPUs.
@@ -88,45 +112,96 @@ impl CPU {
         self.interrupt.send_sipi();
     }
 
-    pub unsafe fn set_rsp0(&mut self, rsp: u64) {
-        self.tss.set_rsp0(rsp);
+    pub unsafe fn load_interrupt_stack(self: Pin<&mut Self>, rsp: u64) {
+        unsafe {
+            self.map_unchecked_mut(|me| &mut me.tss).set_rsp0(rsp);
+        }
     }
 
-    pub unsafe fn set_tls32(&mut self, desc: GDTEntry) {
-        self.gdt.set_tls32(desc);
+    pub fn set_tls32(self: Pin<&mut Self>, user_tls: &UserTLS) {
+        let UserTLS::TLS32 { desc, base } = user_tls else {
+            unimplemented!("TLS64 is not supported yet")
+        };
+
+        unsafe {
+            // SAFETY: We don't move the GDT object.
+            self.get_unchecked_mut().gdt.set_tls32(*desc);
+        }
+
+        const IA32_KERNEL_GS_BASE: u32 = 0xc0000102;
+        arch::wrmsr(IA32_KERNEL_GS_BASE, *base);
     }
 
     pub fn cpuid(&self) -> usize {
         self.cpuid
     }
+
+    pub fn end_of_interrupt(self: Pin<&mut Self>) {
+        unsafe {
+            // SAFETY: We don't move the `interrupt` field out.
+            self.map_unchecked_mut(|me| &mut me.interrupt)
+                .end_of_interrupt();
+        }
+    }
+
+    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()))
+        }
+    }
+}
+
+impl TSS {
+    pub fn new() -> Self {
+        Self {
+            _reserved1: 0,
+            rsp: [TSS_SP { low: 0, high: 0 }; 3],
+            _reserved2: 0,
+            _reserved3: 0,
+            ist: [TSS_SP { low: 0, high: 0 }; 7],
+            _reserved4: 0,
+            _reserved5: 0,
+            _reserved6: 0,
+            iomap_base: 0,
+            _pinned: PhantomPinned,
+        }
+    }
+
+    pub fn set_rsp0(self: Pin<&mut Self>, rsp: u64) {
+        unsafe {
+            // SAFETY: We don't move the TSS object.
+            let me = self.get_unchecked_mut();
+            me.rsp[0].low = rsp as u32;
+            me.rsp[0].high = (rsp >> 32) as u32;
+        }
+    }
 }
 
 #[macro_export]
 macro_rules! define_smp_bootstrap {
     ($cpu_count:literal, $ap_entry:ident, $alloc_kstack:tt) => {
-        #[no_mangle]
         static BOOT_SEMAPHORE: core::sync::atomic::AtomicU64 =
             core::sync::atomic::AtomicU64::new(0);
-        #[no_mangle]
         static BOOT_STACK: core::sync::atomic::AtomicU64 =
             core::sync::atomic::AtomicU64::new(0);
 
-        #[no_mangle]
         static CPU_COUNT: core::sync::atomic::AtomicU64 =
-            core::sync::atomic::AtomicU64::new(0);
+            core::sync::atomic::AtomicU64::new(1);
 
         core::arch::global_asm!(
             r#"
-        .pushsection .stage1.smp
+        .pushsection .stage1.smp, "ax", @progbits
         .code16
         .globl ap_bootstrap
         .type ap_bootstrap, @function
         ap_bootstrap:
-            ljmp $0x0, $.Lap1
+            ljmp $0x0, $2f
 
-        .Lap1:
+        2:
             # we use the shared gdt for cpu bootstrapping
-            lgdt .Lshared_gdt_desc
+            lgdt EARLY_GDT_DESCRIPTOR
 
             # set msr
             mov $0xc0000080, %ecx
@@ -148,14 +223,10 @@ macro_rules! define_smp_bootstrap {
             or $0x80010001, %eax
             mov %eax, %cr0
 
-            ljmp $0x08, $.Lap_bootstrap_end
-
-        .align 16
-        .Lshared_gdt_desc:
-            .8byte 0x0000000000005f
+            ljmp $0x08, $2f
 
         .code64
-        .Lap_bootstrap_end:
+        2:
             mov $0x10, %ax
             mov %ax, %ds
             mov %ax, %es
@@ -164,21 +235,21 @@ macro_rules! define_smp_bootstrap {
             xor %rsp, %rsp
             xor %rax, %rax
             inc %rax
-        1:
+        2:
             xchg %rax, {BOOT_SEMAPHORE}
             cmp $0, %rax
-            je 1f
+            je 2f
             pause
-            jmp 1b
+            jmp 2b
 
-        1:
+        2:
             mov {BOOT_STACK}, %rsp # Acquire
             cmp $0, %rsp
-            jne 1f
+            jne 2f
             pause
-            jmp 1b
+            jmp 2b
 
-        1:
+        2:
             xor %rax, %rax
             mov %rax, {BOOT_STACK} # Release
             xchg %rax, {BOOT_SEMAPHORE}
@@ -187,10 +258,11 @@ macro_rules! define_smp_bootstrap {
 
             xor %rbp, %rbp
             push %rbp # NULL return address
-            jmp {AP_ENTRY}
+            mov ${AP_ENTRY}, %rax
+            jmp *%rax
             .popsection
             "#,
-            KERNEL_PML4 = const 0x2000,
+            KERNEL_PML4 = const 0x1000,
             BOOT_SEMAPHORE = sym BOOT_SEMAPHORE,
             BOOT_STACK = sym BOOT_STACK,
             CPU_COUNT = sym CPU_COUNT,
@@ -200,7 +272,7 @@ macro_rules! define_smp_bootstrap {
 
         pub unsafe fn wait_cpus_online() {
             use core::sync::atomic::Ordering;
-            while CPU_COUNT.load(Ordering::Acquire) != $cpu_count - 1 {
+            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);

+ 28 - 23
arch/src/x86_64/gdt.rs → crates/eonix_hal/src/arch/x86_64/gdt.rs

@@ -1,11 +1,11 @@
-use crate::TSS;
-use core::arch::asm;
+use super::cpu::TSS;
+use core::{arch::asm, marker::PhantomPinned};
 
 #[repr(transparent)]
 #[derive(Debug, Clone, Copy)]
 pub struct GDTEntry(u64);
 
-pub struct GDT([GDTEntry; GDT::LEN]);
+pub struct GDT([GDTEntry; GDT::LEN], PhantomPinned);
 
 impl GDTEntry {
     const NULL: Self = Self(0);
@@ -50,18 +50,21 @@ impl GDT {
     const TSS_INDEX: usize = 8;
 
     pub fn new() -> Self {
-        Self([
-            GDTEntry::NULL,
-            GDTEntry::KERNEL_CODE64,
-            GDTEntry::KERNEL_DATA64,
-            GDTEntry::USER_CODE64,
-            GDTEntry::USER_DATA64,
-            GDTEntry::USER_CODE32,
-            GDTEntry::USER_DATA32,
-            GDTEntry::NULL, // User TLS 32bit
-            GDTEntry::NULL, // TSS Descriptor Low
-            GDTEntry::NULL, // TSS Descriptor High
-        ])
+        Self(
+            [
+                GDTEntry::NULL,
+                GDTEntry::KERNEL_CODE64,
+                GDTEntry::KERNEL_DATA64,
+                GDTEntry::USER_CODE64,
+                GDTEntry::USER_DATA64,
+                GDTEntry::USER_CODE32,
+                GDTEntry::USER_DATA32,
+                GDTEntry::NULL, // User TLS 32bit
+                GDTEntry::NULL, // TSS Descriptor Low
+                GDTEntry::NULL, // TSS Descriptor High
+            ],
+            PhantomPinned,
+        )
     }
 
     pub fn set_tss(&mut self, base: u64) {
@@ -74,18 +77,20 @@ impl GDT {
         self.0[Self::TLS32_INDEX] = desc;
     }
 
-    pub unsafe fn load(&self) {
+    pub fn load(&self) {
         let len = Self::LEN * 8 - 1;
         let descriptor: [u64; 2] = [(len as u64) << 48, self.0.as_ptr() as u64];
         assert!(len < 0x10000, "GDT too large");
 
         let descriptor_address = &descriptor as *const _ as usize + 6;
-        asm!(
-            "lgdt ({})",
-            "ltr %ax",
-            in(reg) descriptor_address,
-            in("ax") Self::TSS_INDEX as u16 * 8,
-            options(att_syntax)
-        );
+        unsafe {
+            asm!(
+                "lgdt ({})",
+                "ltr %ax",
+                in(reg) descriptor_address,
+                in("ax") Self::TSS_INDEX as u16 * 8,
+                options(att_syntax, readonly, preserves_flags),
+            );
+        }
     }
 }

+ 196 - 0
crates/eonix_hal/src/arch/x86_64/interrupt.rs

@@ -0,0 +1,196 @@
+use arch::{pause, rdmsr};
+use core::{arch::asm, marker::PhantomPinned, pin::Pin, ptr::NonNull};
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+struct IDTEntry {
+    offset_low: u16,
+    selector: u16,
+
+    interrupt_stack: u8,
+    attributes: u8,
+
+    offset_mid: u16,
+    offset_high: u32,
+    reserved: u32,
+}
+
+pub struct APICReg(*mut u32);
+pub struct APICRegs {
+    base: NonNull<u32>,
+}
+
+/// Architecture-specific interrupt control block.
+pub struct InterruptControl {
+    idt: [IDTEntry; 256],
+    apic_base: APICRegs,
+    _pinned: PhantomPinned,
+}
+
+impl IDTEntry {
+    const fn new(offset: usize, selector: u16, attributes: u8) -> Self {
+        Self {
+            offset_low: offset as u16,
+            selector,
+            interrupt_stack: 0,
+            attributes,
+            offset_mid: (offset >> 16) as u16,
+            offset_high: (offset >> 32) as u32,
+            reserved: 0,
+        }
+    }
+
+    const fn null() -> Self {
+        Self {
+            offset_low: 0,
+            selector: 0,
+            interrupt_stack: 0,
+            attributes: 0,
+            offset_mid: 0,
+            offset_high: 0,
+            reserved: 0,
+        }
+    }
+}
+
+impl APICReg {
+    fn new(pointer: *mut u32) -> Self {
+        Self(pointer)
+    }
+
+    pub fn read(&self) -> u32 {
+        unsafe { self.0.read_volatile() }
+    }
+
+    pub fn write(&self, value: u32) {
+        unsafe { self.0.write_volatile(value) }
+    }
+}
+
+impl APICRegs {
+    pub fn local_apic_id(&self) -> APICReg {
+        unsafe { APICReg::new(self.base.byte_offset(0x20).as_ptr()) }
+    }
+
+    pub fn task_priority(&self) -> APICReg {
+        unsafe { APICReg::new(self.base.byte_offset(0x80).as_ptr()) }
+    }
+
+    pub fn end_of_interrupt(&self) {
+        unsafe { APICReg::new(self.base.byte_offset(0xb0).as_ptr()).write(0) }
+    }
+
+    pub fn spurious(&self) -> APICReg {
+        unsafe { APICReg::new(self.base.byte_offset(0xf0).as_ptr()) }
+    }
+
+    pub fn interrupt_command(&self) -> APICReg {
+        unsafe { APICReg::new(self.base.byte_offset(0x300).as_ptr()) }
+    }
+
+    pub fn timer_register(&self) -> APICReg {
+        unsafe { APICReg::new(self.base.byte_offset(0x320).as_ptr()) }
+    }
+
+    pub fn timer_initial_count(&self) -> APICReg {
+        unsafe { APICReg::new(self.base.byte_offset(0x380).as_ptr()) }
+    }
+
+    pub fn timer_current_count(&self) -> APICReg {
+        unsafe { APICReg::new(self.base.byte_offset(0x390).as_ptr()) }
+    }
+
+    pub fn timer_divide(&self) -> APICReg {
+        unsafe { APICReg::new(self.base.byte_offset(0x3e0).as_ptr()) }
+    }
+}
+
+impl InterruptControl {
+    /// # Return
+    /// Returns a tuple of InterruptControl and the cpu id of the current cpu.
+    pub fn new() -> (Self, usize) {
+        let trap_stubs_base = super::trap::trap_stubs_start as usize;
+
+        let idt = core::array::from_fn(|idx| match idx {
+            0..0x80 => IDTEntry::new(trap_stubs_base + 8 * idx, 0x08, 0x8e),
+            0x80 => IDTEntry::new(trap_stubs_base + 8 * idx, 0x08, 0xee),
+            _ => IDTEntry::null(),
+        });
+
+        let apic_base = {
+            let apic_base = rdmsr(0x1b);
+            assert_eq!(apic_base & 0x800, 0x800, "LAPIC not enabled");
+
+            let apic_base = ((apic_base & !0xfff) + 0xffffff00_00000000) as *mut u32;
+            APICRegs {
+                // TODO: A better way to convert to physical address
+                base: NonNull::new(apic_base).expect("Invalid APIC base"),
+            }
+        };
+
+        // Make sure APIC is enabled.
+        apic_base.spurious().write(0x1ff);
+
+        let cpuid = apic_base.local_apic_id().read() >> 24;
+
+        (
+            Self {
+                idt,
+                apic_base,
+                _pinned: PhantomPinned,
+            },
+            cpuid as usize,
+        )
+    }
+
+    pub fn setup_timer(&self) {
+        self.apic_base.task_priority().write(0);
+        self.apic_base.timer_divide().write(0x3); // Divide by 16
+        self.apic_base.timer_register().write(0x20040);
+
+        // TODO: Get the bus frequency from...?
+        let freq = 200;
+        let count = freq * 1_000_000 / 16 / 100;
+        self.apic_base.timer_initial_count().write(count as u32);
+    }
+
+    pub fn setup_idt(self: Pin<&mut Self>) {
+        lidt(
+            self.idt.as_ptr() as usize,
+            (size_of::<IDTEntry>() * 256 - 1) as u16,
+        );
+    }
+
+    pub fn send_sipi(&self) {
+        let icr = self.apic_base.interrupt_command();
+
+        icr.write(0xc4500);
+        while icr.read() & 0x1000 != 0 {
+            pause();
+        }
+
+        icr.write(0xc4606);
+        while icr.read() & 0x1000 != 0 {
+            pause();
+        }
+    }
+
+    /// Send EOI to APIC so that it can send more interrupts.
+    pub fn end_of_interrupt(&self) {
+        self.apic_base.end_of_interrupt()
+    }
+}
+
+fn lidt(base: usize, limit: u16) {
+    let mut idt_descriptor = [0u16; 5];
+
+    idt_descriptor[0] = limit;
+    idt_descriptor[1] = base as u16;
+    idt_descriptor[2] = (base >> 16) as u16;
+    idt_descriptor[3] = (base >> 32) as u16;
+    idt_descriptor[4] = (base >> 48) as u16;
+
+    unsafe {
+        asm!("lidt ({})", in(reg) &idt_descriptor, options(att_syntax, nostack, preserves_flags));
+    }
+}

+ 98 - 0
crates/eonix_hal/src/arch/x86_64/link.x

@@ -0,0 +1,98 @@
+SECTIONS {
+    .low 0x500 (NOLOAD) :
+    {
+
+        KEEP(*(.low .low*));
+
+    } > LOWMEM
+
+    .mbr 0xe00 :
+    {
+        KEEP(*(.mbr));
+
+        /* avoid the MBR being overwritten */
+        . = ABSOLUTE(ADDR(.mbr) + 446);
+
+        /* avoid the MBR being overwritten */
+        . = ABSOLUTE(ADDR(.mbr) + 510);
+        BYTE(0x55);
+        BYTE(0xaa);
+    } > LOWMEM = 0x00
+
+    .stage1 0x6000 :
+    {
+        KEEP(*(.stage1.smp));
+
+        . = ALIGN(16);
+        KEEP(*(.stage1));
+
+        . = ABSOLUTE(ADDR(.stage1) + 512 * 7 - 4);
+        STAGE1_MAGIC = .;
+        LONG(ABSOLUTE(STAGE1_MAGIC_VALUE));
+
+        STAGE1_MAGIC_VALUE = 0x01145140;
+    } > LOWMEM AT> LOWMEM
+}
+
+SECTIONS {
+    .text.syscall_fns :
+    {
+
+        KEEP(*(.syscall_fns*));
+
+    } > REGION_TEXT
+}
+INSERT AFTER .text;
+
+SECTIONS {
+    .rodata.fixups :
+    {
+        . = ALIGN(16);
+        FIX_START = .;
+
+        KEEP(*(.fix));
+
+        FIX_END = .;
+    } > REGION_RODATA
+
+    .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
+}
+INSERT AFTER .rodata;
+
+SECTIONS {
+    .percpu 0 : ALIGN(16)
+    {
+        __spercpu = .;
+
+        PERCPU_START = .;
+        QUAD(0); /* Reserved for x86 percpu pointer */
+
+        . = ALIGN(16);
+
+        *(.percpu .percpu*);
+
+        . = ALIGN(16);
+        __epercpu = .;
+    } > LOWMEM AT> REGION_RODATA
+
+    PERCPU_DATA_START = LOADADDR(.percpu);
+    PERCPU_LENGTH = ABSOLUTE(__epercpu - __spercpu);
+
+    KIMAGE_PAGES = (__edata - _stext + 0x1000 - 1) / 0x1000;
+    KIMAGE_32K_COUNT = (KIMAGE_PAGES + 8 - 1) / 8;
+
+    BSS_LENGTH = ABSOLUTE(__ebss - __sbss);
+}
+INSERT AFTER .rodata;

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

@@ -0,0 +1,11 @@
+MEMORY {
+    LOWMEM : org = 0x0000000000000000, len = 1M
+    KBSS   : org = 0xffffffffc0200000, len = 2M
+    KIMAGE : org = 0xffffffffffc00000, 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);

+ 375 - 0
crates/eonix_hal/src/arch/x86_64/mm.rs

@@ -0,0 +1,375 @@
+use crate::traits::mm::Memory;
+use core::{marker::PhantomData, ptr::NonNull};
+use eonix_mm::{
+    address::{Addr as _, AddrOps as _, PAddr, PRange, PhysAccess, VAddr},
+    page_table::{
+        PageAttribute, PageTable, PageTableLevel, PagingMode, RawAttribute, RawPageTable,
+        TableAttribute, PTE,
+    },
+    paging::{NoAlloc, Page, PageBlock, PAGE_SIZE, PFN},
+};
+use eonix_sync_base::LazyLock;
+
+pub const PA_P: u64 = 0x001;
+pub const PA_RW: u64 = 0x002;
+pub const PA_US: u64 = 0x004;
+#[allow(dead_code)]
+pub const PA_PWT: u64 = 0x008;
+#[allow(dead_code)]
+pub const PA_PCD: u64 = 0x010;
+pub const PA_A: u64 = 0x020;
+pub const PA_D: u64 = 0x040;
+pub const PA_PS: u64 = 0x080;
+pub const PA_G: u64 = 0x100;
+pub const PA_COW: u64 = 0x200;
+pub const PA_MMAP: u64 = 0x400;
+pub const PA_ANON: u64 = 0x800;
+pub const PA_NXE: u64 = 0x8000_0000_0000_0000;
+pub const PA_MASK: u64 = 0xfff0_0000_0000_0fff;
+
+pub const P_KIMAGE_START: PAddr = PAddr::from_val(0x200000);
+pub const V_KERNEL_BSS_START: VAddr = VAddr::from(0xffffffffc0200000);
+
+const KERNEL_PML4_PFN: PFN = PFN::from_val(0x1000 >> 12);
+
+pub static GLOBAL_PAGE_TABLE: LazyLock<PageTable<ArchPagingMode, NoAlloc, ArchPhysAccess>> =
+    LazyLock::new(|| unsafe {
+        Page::with_raw(KERNEL_PML4_PFN, |root_table_page| {
+            PageTable::with_root_table(root_table_page.clone())
+        })
+    });
+
+#[repr(transparent)]
+pub struct PTE64(u64);
+
+#[derive(Clone, Copy)]
+pub struct PageAttribute64(u64);
+
+pub struct RawPageTable4Levels<'a>(NonNull<PTE64>, PhantomData<&'a ()>);
+
+pub struct PagingMode4Levels;
+
+pub struct ArchPhysAccess;
+
+pub struct ArchMemory;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct E820MemMapEntry {
+    base: u64,
+    len: u64,
+    entry_type: u32,
+    acpi_attrs: u32,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct BootLoaderData {
+    entry_count: u32,
+    entry_length: u32,
+
+    block_count_1k: u32,
+    block_count_64k: u32,
+
+    all_entries: [E820MemMapEntry; 42],
+}
+
+impl PTE for PTE64 {
+    type Attr = PageAttribute64;
+
+    fn set(&mut self, pfn: PFN, attr: Self::Attr) {
+        let paddr = PAddr::from(pfn).addr();
+
+        self.0 = (paddr 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),
+        )
+    }
+}
+
+impl PagingMode for PagingMode4Levels {
+    type Entry = PTE64;
+    type RawTable<'a> = RawPageTable4Levels<'a>;
+
+    const LEVELS: &'static [PageTableLevel] = &[
+        PageTableLevel::new(39, 9),
+        PageTableLevel::new(30, 9),
+        PageTableLevel::new(21, 9),
+        PageTableLevel::new(12, 9),
+    ];
+}
+
+impl<'a> RawPageTable<'a> for RawPageTable4Levels<'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 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_PS != 0 {
+            panic!("Encountered a huge page while parsing table attributes");
+        }
+
+        if self.0 & PA_P != 0 {
+            table_attr |= TableAttribute::PRESENT;
+        }
+        if self.0 & PA_G != 0 {
+            table_attr |= TableAttribute::GLOBAL;
+        }
+        if self.0 & PA_US != 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::READ;
+
+        if self.0 & PA_P != 0 {
+            page_attr |= PageAttribute::PRESENT;
+        }
+
+        if self.0 & PA_RW != 0 {
+            page_attr |= PageAttribute::WRITE;
+        }
+
+        if self.0 & PA_NXE == 0 {
+            page_attr |= PageAttribute::EXECUTE;
+        }
+
+        if self.0 & PA_US != 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;
+        }
+
+        if self.0 & PA_PS != 0 {
+            page_attr |= PageAttribute::HUGE;
+        }
+
+        Some(page_attr)
+    }
+}
+
+impl From<PageAttribute> for PageAttribute64 {
+    fn from(page_attr: PageAttribute) -> Self {
+        let mut raw_attr = PA_NXE;
+
+        for attr in page_attr.iter() {
+            match attr {
+                PageAttribute::PRESENT => raw_attr |= PA_P,
+                PageAttribute::READ => {}
+                PageAttribute::WRITE => raw_attr |= PA_RW,
+                PageAttribute::EXECUTE => raw_attr &= !PA_NXE,
+                PageAttribute::USER => raw_attr |= PA_US,
+                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 => raw_attr |= PA_ANON,
+                PageAttribute::HUGE => raw_attr |= PA_PS,
+                _ => unreachable!("Invalid page attribute"),
+            }
+        }
+
+        Self(raw_attr)
+    }
+}
+
+impl From<TableAttribute> for PageAttribute64 {
+    fn from(table_attr: TableAttribute) -> Self {
+        let mut raw_attr = PA_RW;
+
+        for attr in table_attr.iter() {
+            match attr {
+                TableAttribute::PRESENT => raw_attr |= PA_P,
+                TableAttribute::GLOBAL => raw_attr |= PA_G,
+                TableAttribute::USER => raw_attr |= PA_US,
+                TableAttribute::ACCESSED => raw_attr |= PA_A,
+                _ => unreachable!("Invalid table attribute"),
+            }
+        }
+
+        Self(raw_attr)
+    }
+}
+
+pub type ArchPagingMode = PagingMode4Levels;
+
+impl ArchPhysAccess {
+    const PHYS_OFFSET: usize = 0xffff_ff00_0000_0000;
+}
+
+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 E820MemMapEntry {
+    const ENTRY_FREE: u32 = 1;
+    // const ENTRY_USED: u32 = 2;
+
+    const fn zeroed() -> Self {
+        Self {
+            base: 0,
+            len: 0,
+            entry_type: 0,
+            acpi_attrs: 0,
+        }
+    }
+
+    fn is_free(&self) -> bool {
+        self.entry_type == Self::ENTRY_FREE
+    }
+
+    // fn is_used(&self) -> bool {
+    //     self.entry_type == Self::ENTRY_USED
+    // }
+
+    fn range(&self) -> PRange {
+        PRange::from(PAddr::from(self.base as usize)).grow(self.len as usize)
+    }
+}
+
+impl BootLoaderData {
+    const fn zeroed() -> Self {
+        Self {
+            entry_count: 0,
+            entry_length: 0,
+            block_count_1k: 0,
+            block_count_64k: 0,
+            all_entries: [E820MemMapEntry::zeroed(); 42],
+        }
+    }
+
+    // fn memory_size(&self) -> usize {
+    //     // The initial 1M is not counted in the E820 map. We add them to the total as well.
+    //     ((self.block_count_1k + 64 * self.block_count_64k) * 1024 + 1 * 1024 * 1024) as usize
+    // }
+
+    fn entries(&self) -> &[E820MemMapEntry] {
+        &self.all_entries[..self.entry_count as usize]
+    }
+
+    fn free_entries(&self) -> impl Iterator<Item = &E820MemMapEntry> {
+        self.entries().iter().filter(|entry| entry.is_free())
+    }
+}
+
+#[unsafe(link_section = ".low")]
+pub static mut E820_MEM_MAP_DATA: BootLoaderData = BootLoaderData::zeroed();
+
+impl Memory for ArchMemory {
+    fn present_ram() -> impl Iterator<Item = PRange> {
+        let e820 = &raw const E820_MEM_MAP_DATA;
+
+        unsafe {
+            // SAFETY: We don't write to the E820 memory map after the bootstrap.
+            e820.as_ref()
+                .unwrap_unchecked()
+                .free_entries()
+                .map(|entry| entry.range())
+        }
+    }
+
+    fn free_ram() -> impl Iterator<Item = PRange> {
+        unsafe extern "C" {
+            fn KIMAGE_PAGES();
+        }
+
+        let kimage_pages = KIMAGE_PAGES as usize;
+
+        let paddr_after_kimage = P_KIMAGE_START + kimage_pages * PAGE_SIZE;
+        let paddr_after_kimage_aligned = paddr_after_kimage.ceil_to(0x200000);
+        let paddr_unused_start = paddr_after_kimage_aligned;
+
+        core::iter::once(PRange::new(
+            PAddr::from_val(0x100000),
+            PAddr::from_val(0x200000),
+        ))
+        .chain(core::iter::once(PRange::new(
+            paddr_after_kimage,
+            paddr_after_kimage_aligned,
+        )))
+        .chain(
+            Self::present_ram()
+                .filter(move |range| range.end() > paddr_unused_start)
+                .map(move |range| {
+                    if range.start() < paddr_unused_start {
+                        let (_, right) = range.split_at(paddr_unused_start);
+                        right
+                    } else {
+                        range
+                    }
+                }),
+        )
+    }
+}

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

@@ -0,0 +1,7 @@
+pub mod bootstrap;
+pub mod context;
+pub mod cpu;
+pub mod gdt;
+pub mod interrupt;
+pub mod mm;
+pub mod trap;

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

@@ -0,0 +1,390 @@
+mod trap_context;
+
+use super::context::TaskContext;
+use core::arch::{asm, global_asm, naked_asm};
+use eonix_hal_traits::{
+    context::RawTaskContext,
+    trap::{IrqState as IrqStateTrait, TrapReturn},
+};
+
+pub use trap_context::TrapContext;
+
+unsafe extern "C" {
+    fn _default_trap_handler(trap_context: &mut TrapContext);
+    pub fn trap_stubs_start();
+    fn _raw_trap_return();
+}
+
+#[eonix_percpu::define_percpu]
+static TRAP_HANDLER: unsafe extern "C" fn() = default_trap_handler;
+
+#[eonix_percpu::define_percpu]
+static CAPTURER_CONTEXT: TaskContext = TaskContext::new();
+
+/// This value will never be used.
+static mut DIRTY_TRAP_CONTEXT: TaskContext = TaskContext::new();
+
+/// State of the interrupt flag.
+pub struct IrqState(u64);
+
+global_asm!(
+    r"
+    .set RAX, 0x00
+    .set RBX, 0x08
+    .set RCX, 0x10
+    .set RDX, 0x18
+    .set RDI, 0x20
+    .set RSI, 0x28
+    .set R8, 0x30
+    .set R9, 0x38
+    .set R10, 0x40
+    .set R11, 0x48
+    .set R12, 0x50
+    .set R13, 0x58
+    .set R14, 0x60
+    .set R15, 0x68
+    .set RBP, 0x70
+    .set INT_NO, 0x78
+    .set ERRCODE, 0x80
+    .set RIP, 0x88
+    .set CS, 0x90
+    .set FLAGS, 0x98
+    .set RSP, 0xa0
+    .set SS, 0xa8
+
+    .macro cfi_all_same_value
+        .cfi_same_value %rax
+        .cfi_same_value %rbx
+        .cfi_same_value %rcx
+        .cfi_same_value %rdx
+        .cfi_same_value %rdi
+        .cfi_same_value %rsi
+        .cfi_same_value %r8
+        .cfi_same_value %r9
+        .cfi_same_value %r10
+        .cfi_same_value %r11
+        .cfi_same_value %r12
+        .cfi_same_value %r13
+        .cfi_same_value %r14
+        .cfi_same_value %r15
+        .cfi_same_value %rbp
+    .endm
+
+    .globl {trap_stubs_start}
+    {trap_stubs_start}:
+        .altmacro
+        .macro build_isr_no_err name
+            .align 8
+            .globl ISR\name
+            .type  ISR\name @function
+            ISR\name:
+                .cfi_startproc
+                .cfi_signal_frame
+                .cfi_def_cfa_offset 0x08
+                .cfi_offset %rsp, 0x10
+
+                cfi_all_same_value
+
+                push %rbp # push placeholder for error code
+                .cfi_def_cfa_offset 0x10
+
+                call _raw_trap_entry
+                .cfi_endproc
+        .endm
+
+        .altmacro
+        .macro build_isr_err name
+            .align 8
+            .globl ISR\name
+            .type  ISR\name @function
+            ISR\name:
+                .cfi_startproc
+                .cfi_signal_frame
+                .cfi_def_cfa_offset 0x10
+                .cfi_offset %rsp, 0x10
+
+                cfi_all_same_value
+
+                call _raw_trap_entry
+                .cfi_endproc
+        .endm
+
+        build_isr_no_err 0
+        build_isr_no_err 1
+        build_isr_no_err 2
+        build_isr_no_err 3
+        build_isr_no_err 4
+        build_isr_no_err 5
+        build_isr_no_err 6
+        build_isr_no_err 7
+        build_isr_err    8
+        build_isr_no_err 9
+        build_isr_err    10
+        build_isr_err    11
+        build_isr_err    12
+        build_isr_err    13
+        build_isr_err    14
+        build_isr_no_err 15
+        build_isr_no_err 16
+        build_isr_err    17
+        build_isr_no_err 18
+        build_isr_no_err 19
+        build_isr_no_err 20
+        build_isr_err    21
+        build_isr_no_err 22
+        build_isr_no_err 23
+        build_isr_no_err 24
+        build_isr_no_err 25
+        build_isr_no_err 26
+        build_isr_no_err 27
+        build_isr_no_err 28
+        build_isr_err    29
+        build_isr_err    30
+        build_isr_no_err 31
+
+        .set i, 32
+        .rept 0x80+1
+            build_isr_no_err %i
+            .set i, i+1
+        .endr
+    
+    .globl _raw_trap_entry
+    .type  _raw_trap_entry @function
+    _raw_trap_entry:
+        .cfi_startproc
+        .cfi_signal_frame
+        .cfi_def_cfa %rsp, 0x18
+        .cfi_offset %rsp, 0x10
+
+        cfi_all_same_value
+        
+        sub $0x78, %rsp
+        .cfi_def_cfa_offset CS
+        
+        mov %rax, RAX(%rsp)
+        .cfi_rel_offset %rax, RAX
+        mov %rbx, RBX(%rsp)
+        .cfi_rel_offset %rbx, RBX
+        mov %rcx, RCX(%rsp)
+        .cfi_rel_offset %rcx, RCX
+        mov %rdx, RDX(%rsp)
+        .cfi_rel_offset %rdx, RDX
+        mov %rdi, RDI(%rsp)
+        .cfi_rel_offset %rdi, RDI
+        mov %rsi, RSI(%rsp)
+        .cfi_rel_offset %rsi, RSI
+        mov %r8, R8(%rsp)
+        .cfi_rel_offset %r8, R8
+        mov %r9, R9(%rsp)
+        .cfi_rel_offset %r9, R9
+        mov %r10, R10(%rsp)
+        .cfi_rel_offset %r10, R10
+        mov %r11, R11(%rsp)
+        .cfi_rel_offset %r11, R11
+        mov %r12, R12(%rsp)
+        .cfi_rel_offset %r12, R12
+        mov %r13, R13(%rsp)
+        .cfi_rel_offset %r13, R13
+        mov %r14, R14(%rsp)
+        .cfi_rel_offset %r14, R14
+        mov %r15, R15(%rsp)
+        .cfi_rel_offset %r15, R15
+        mov %rbp, RBP(%rsp)
+        .cfi_rel_offset %rbp, RBP
+        
+        mov INT_NO(%rsp), %rcx
+        sub ${trap_stubs_start}, %rcx
+        shr $3, %rcx
+        mov %rcx, INT_NO(%rsp)
+        
+        cmpq $0x08, CS(%rsp)
+        je 2f
+        swapgs
+        
+        2:
+        mov %gs:0, %rcx
+        add ${handler}, %rcx
+        mov (%rcx), %rcx
+        
+        jmp *%rcx
+        .cfi_endproc
+    
+    _raw_trap_return:
+        .cfi_startproc
+        .cfi_def_cfa %rsp, CS
+        .cfi_rel_offset %rax, RAX
+        .cfi_rel_offset %rbx, RBX
+        .cfi_rel_offset %rcx, RCX
+        .cfi_rel_offset %rdx, RDX
+        .cfi_rel_offset %rdi, RDI
+        .cfi_rel_offset %rsi, RSI
+        .cfi_rel_offset %r8, R8
+        .cfi_rel_offset %r9, R9
+        .cfi_rel_offset %r10, R10
+        .cfi_rel_offset %r11, R11
+        .cfi_rel_offset %r12, R12
+        .cfi_rel_offset %r13, R13
+        .cfi_rel_offset %r14, R14
+        .cfi_rel_offset %r15, R15
+        .cfi_rel_offset %rbp, RBP
+        .cfi_rel_offset %rsp, RSP
+        
+        mov RAX(%rsp), %rax
+        .cfi_restore %rax
+        mov RBX(%rsp), %rbx
+        .cfi_restore %rbx
+        mov RCX(%rsp), %rcx
+        .cfi_restore %rcx
+        mov RDX(%rsp), %rdx
+        .cfi_restore %rdx
+        mov RDI(%rsp), %rdi
+        .cfi_restore %rdi
+        mov RSI(%rsp), %rsi
+        .cfi_restore %rsi
+        mov R8(%rsp), %r8
+        .cfi_restore %r8
+        mov R9(%rsp), %r9
+        .cfi_restore %r9
+        mov R10(%rsp), %r10
+        .cfi_restore %r10
+        mov R11(%rsp), %r11
+        .cfi_restore %r11
+        mov R12(%rsp), %r12
+        .cfi_restore %r12
+        mov R13(%rsp), %r13
+        .cfi_restore %r13
+        mov R14(%rsp), %r14
+        .cfi_restore %r14
+        mov R15(%rsp), %r15
+        .cfi_restore %r15
+        mov RBP(%rsp), %rbp
+        .cfi_restore %rbp
+        
+        cmpq $0x08, CS(%rsp)
+        je 2f
+        swapgs
+        
+        2:
+        lea RIP(%rsp), %rsp
+        .cfi_def_cfa %rsp, 0x08
+        .cfi_offset %rsp, 0x10
+        
+        iretq
+        .cfi_endproc
+    ",
+    trap_stubs_start = sym trap_stubs_start,
+    handler = sym _percpu_inner_TRAP_HANDLER,
+    options(att_syntax),
+);
+
+/// Default handler handles the trap on the current stack and returns
+/// to the context before interrut.
+#[unsafe(naked)]
+unsafe extern "C" fn default_trap_handler() {
+    naked_asm!(
+        ".cfi_startproc",
+        "mov %rsp, %rbx",
+        ".cfi_def_cfa_register %rbx",
+        "",
+        "and $~0xf, %rsp",
+        "",
+        "mov %rbx, %rdi",
+        "call {handle_trap}",
+        "",
+        "mov %rbx, %rsp",
+        ".cfi_def_cfa_register %rsp",
+        "",
+        "jmp {trap_return}",
+        ".cfi_endproc",
+        handle_trap = sym _default_trap_handler,
+        trap_return = sym _raw_trap_return,
+        options(att_syntax),
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn captured_trap_handler() {
+    naked_asm!(
+        "mov ${from_context}, %rdi",
+        "mov %gs:0, %rsi",
+        "add ${to_context}, %rsi",
+        "",
+        "mov %rdi, %rsp", // We need a temporary stack to use `switch()`.
+        "",
+        "jmp {switch}",
+        from_context = sym DIRTY_TRAP_CONTEXT,
+        to_context = sym _percpu_inner_CAPTURER_CONTEXT,
+        switch = sym TaskContext::switch,
+        options(att_syntax),
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn captured_trap_return(trap_context: usize) -> ! {
+    naked_asm!(
+        "jmp {trap_return}",
+        trap_return = sym _raw_trap_return,
+        options(att_syntax),
+    );
+}
+
+impl TrapReturn for TrapContext {
+    unsafe fn trap_return(&mut self) {
+        let irq_states = disable_irqs_save();
+        let old_handler = TRAP_HANDLER.swap(captured_trap_handler);
+
+        let mut to_ctx = TaskContext::new();
+        to_ctx.set_program_counter(captured_trap_return as _);
+        to_ctx.set_stack_pointer(&raw mut *self as usize);
+        to_ctx.set_interrupt_enabled(false);
+
+        unsafe {
+            TaskContext::switch(CAPTURER_CONTEXT.as_mut(), &mut to_ctx);
+        }
+
+        TRAP_HANDLER.set(old_handler);
+        irq_states.restore();
+    }
+}
+
+impl IrqStateTrait for IrqState {
+    fn restore(self) {
+        let Self(state) = self;
+
+        unsafe {
+            asm!(
+                "push {state}",
+                "popf",
+                state = in(reg) state,
+                options(att_syntax, nomem)
+            );
+        }
+    }
+}
+
+pub fn enable_irqs() {
+    unsafe {
+        asm!("sti", options(att_syntax, nomem, nostack));
+    }
+}
+
+pub fn disable_irqs() {
+    unsafe {
+        asm!("cli", options(att_syntax, nomem, nostack));
+    }
+}
+
+pub fn disable_irqs_save() -> IrqState {
+    let state: u64;
+    unsafe {
+        asm!(
+            "pushf",
+            "pop {state}",
+            "cli",
+            state = out(reg) state,
+            options(att_syntax, nomem)
+        );
+    }
+
+    IrqState(state)
+}

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

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

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

@@ -0,0 +1,22 @@
+use crate::mm::{BasicPageAlloc, BasicPageAllocRef};
+use core::cell::RefCell;
+use eonix_mm::address::PRange;
+
+pub struct BootStrapData {
+    pub(crate) early_stack: PRange,
+    pub(crate) allocator: Option<RefCell<BasicPageAlloc>>,
+}
+
+impl BootStrapData {
+    pub fn get_alloc(&self) -> Option<BasicPageAllocRef<'_>> {
+        self.allocator.as_ref().map(BasicPageAllocRef::new)
+    }
+
+    pub fn take_alloc(&mut self) -> Option<BasicPageAlloc> {
+        self.allocator.take().map(RefCell::into_inner)
+    }
+
+    pub fn get_early_stack(&self) -> PRange {
+        self.early_stack
+    }
+}

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

@@ -0,0 +1 @@
+pub use crate::arch::context::TaskContext;

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

@@ -0,0 +1,14 @@
+#![no_std]
+#![feature(allocator_api)]
+#![feature(doc_notable_trait)]
+
+pub(crate) mod arch;
+
+pub mod bootstrap;
+pub mod context;
+pub mod mm;
+pub mod processor;
+pub mod trap;
+
+pub use eonix_hal_macros::{ap_main, default_trap_handler, main};
+pub use eonix_hal_traits as traits;

+ 114 - 0
crates/eonix_hal/src/link.x.in

@@ -0,0 +1,114 @@
+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 :
+    {
+        __stext = .;
+
+        *(.text .text.*);
+
+    } > REGION_TEXT
+
+    __etext = .;
+
+    .rodata : ALIGN(16)
+    {
+        __srodata = .;
+
+        *(.rodata .rodata.*);
+
+    } > REGION_RODATA
+
+    __erodata = .;
+
+    .data : ALIGN(16)
+    {
+        __sdata = .;
+
+        *(.data .data.*);
+        *(.got .got.plt);
+
+    } > REGION_DATA
+
+    __edata = .;
+
+    .bss (NOLOAD) : ALIGN(16)
+    {
+        __sbss = .;
+
+        *(.bss .bss.*);
+
+        . = ALIGN(0x1000);
+    } > REGION_BSS
+
+    __ebss = .;
+
+    .eh_frame : ALIGN(16)
+    {
+        __seh_frame = .;
+
+        KEEP(*(.eh_frame .eh_frame*));
+
+    } > REGION_EHFRAME
+
+    . = ALIGN(0x1000);
+    __eeh_frame = .;
+}
+
+SECTIONS {
+    /* Stabs debugging sections.  */
+    .stab          0 : { KEEP(*(.stab)); }
+    .stabstr       0 : { KEEP(*(.stabstr)); }
+    .stab.excl     0 : { KEEP(*(.stab.excl)); }
+    .stab.exclstr  0 : { KEEP(*(.stab.exclstr)); }
+    .stab.index    0 : { KEEP(*(.stab.index)); }
+    .stab.indexstr 0 : { KEEP(*(.stab.indexstr)); }
+    .comment       0 : { KEEP(*(.comment)); }
+    /* DWARF debug sections.
+       Symbols in the DWARF debugging sections are relative to the beginning
+       of the section so we begin them at 0.  */
+    /* DWARF 1 */
+    .debug          0 : { KEEP(*(.debug)); }
+    .line           0 : { KEEP(*(.line)); }
+    /* GNU DWARF 1 extensions */
+    .debug_srcinfo  0 : { KEEP(*(.debug_srcinfo)); }
+    .debug_sfnames  0 : { KEEP(*(.debug_sfnames)); }
+    /* DWARF 1.1 and DWARF 2 */
+    .debug_aranges  0 : { KEEP(*(.debug_aranges)); }
+    .debug_pubnames 0 : { KEEP(*(.debug_pubnames)); }
+    /* DWARF 2 */
+    .debug_info     0 : { KEEP(*(.debug_info)); }
+    .debug_abbrev   0 : { KEEP(*(.debug_abbrev)); }
+    .debug_line     0 : { KEEP(*(.debug_line)); }
+    .debug_frame    0 : { KEEP(*(.debug_frame)); }
+    .debug_str      0 : { KEEP(*(.debug_str)); }
+    .debug_loc      0 : { KEEP(*(.debug_loc)); }
+    .debug_macinfo  0 : { KEEP(*(.debug_macinfo)); }
+    /* SGI/MIPS DWARF 2 extensions */
+    .debug_weaknames 0 : { KEEP(*(.debug_weaknames)); }
+    .debug_funcnames 0 : { KEEP(*(.debug_funcnames)); }
+    .debug_typenames 0 : { KEEP(*(.debug_typenames)); }
+    .debug_varnames  0 : { KEEP(*(.debug_varnames)); }
+
+    /* DWARF Other */
+    .debug_ranges  0 : { KEEP(*(.debug_ranges)); }
+    .debug_line_str 0 : { KEEP(*(.debug_line_str)); }
+
+    /DISCARD/ :
+    {
+        *(.fini_array*)
+        *(.note*)
+        *(.dtors*)
+        *(.debug_gdb_scripts*)
+    }
+}

+ 198 - 0
crates/eonix_hal/src/mm.rs

@@ -0,0 +1,198 @@
+use core::{
+    alloc::{AllocError, Allocator, Layout},
+    cell::RefCell,
+    ptr::NonNull,
+};
+use eonix_mm::{
+    address::{AddrOps as _, PRange},
+    paging::{PageAlloc, UnmanagedRawPage, PAGE_SIZE, PFN},
+};
+
+pub use crate::arch::mm::{ArchMemory, ArchPagingMode, ArchPhysAccess, GLOBAL_PAGE_TABLE};
+
+pub struct BasicPageAlloc {
+    ranges: [Option<PRange>; Self::MAX],
+    head: usize,
+    tail: usize,
+}
+
+struct ScopedAllocInner<'a> {
+    _memory: &'a mut [u8],
+    current: NonNull<[u8]>,
+    allocated_count: usize,
+}
+
+pub struct ScopedAllocator<'a> {
+    inner: RefCell<ScopedAllocInner<'a>>,
+}
+
+impl BasicPageAlloc {
+    const MAX: usize = 32;
+
+    pub const fn new() -> Self {
+        Self {
+            ranges: [None; Self::MAX],
+            head: 0,
+            tail: 0,
+        }
+    }
+
+    fn alloc_one(&mut self) -> PFN {
+        assert_ne!(self.head, self.tail, "No free pages available");
+        let mut range = self.ranges[self.head].take().unwrap();
+        range = range.shrink(PAGE_SIZE);
+
+        let pfn = PFN::from(range.end());
+
+        if range.len() != 0 {
+            self.ranges[self.head] = Some(range);
+        } else {
+            self.head += 1;
+            self.head %= Self::MAX;
+        }
+
+        pfn
+    }
+
+    fn alloc_order(&mut self, order: u32) -> PFN {
+        assert!(order <= 4);
+        let me = core::mem::replace(self, Self::new());
+
+        let mut found = None;
+        for mut range in me.into_iter() {
+            if found.is_some() || range.len() < (PAGE_SIZE << order) {
+                self.add_range(range);
+                continue;
+            }
+
+            range = range.shrink(PAGE_SIZE << order);
+            found = Some(PFN::from(range.end()));
+
+            if range.len() != 0 {
+                self.add_range(range);
+            }
+        }
+
+        found.expect("No free pages available for the requested order")
+    }
+
+    pub fn add_range(&mut self, range: PRange) {
+        let tail = self.tail;
+
+        self.tail += 1;
+        self.tail %= Self::MAX;
+
+        if self.tail == self.head {
+            panic!("Page allocator is full");
+        }
+
+        self.ranges[tail] = Some(PRange::new(range.start().ceil(), range.end().floor()));
+    }
+
+    pub fn alloc(&mut self, order: u32) -> PFN {
+        match order {
+            0 => self.alloc_one(),
+            ..=4 => self.alloc_order(order),
+            _ => panic!("Order {} is too large for BasicPageAlloc", order),
+        }
+    }
+
+    pub fn into_iter(self) -> impl Iterator<Item = PRange> {
+        self.ranges
+            .into_iter()
+            .cycle()
+            .skip(self.head)
+            .map_while(|x| x)
+    }
+}
+
+#[derive(Clone)]
+pub struct BasicPageAllocRef<'a>(&'a RefCell<BasicPageAlloc>);
+
+impl<'a> BasicPageAllocRef<'a> {
+    pub const fn new(alloc: &'a RefCell<BasicPageAlloc>) -> Self {
+        Self(alloc)
+    }
+}
+
+impl PageAlloc for BasicPageAllocRef<'_> {
+    type RawPage = UnmanagedRawPage;
+
+    fn alloc_order(&self, order: u32) -> Option<Self::RawPage> {
+        Some(Self::RawPage::new(self.0.borrow_mut().alloc(order), order))
+    }
+
+    unsafe fn dealloc(&self, _: Self::RawPage) {
+        panic!("Dealloc is not supported in BasicPageAlloc");
+    }
+
+    fn has_management_over(&self, _: Self::RawPage) -> bool {
+        true
+    }
+}
+
+impl<'a> ScopedAllocator<'a> {
+    pub fn new(memory: &'a mut [u8]) -> Self {
+        ScopedAllocator {
+            inner: RefCell::new(ScopedAllocInner {
+                current: NonNull::new(memory).unwrap(),
+                _memory: memory,
+                allocated_count: 0,
+            }),
+        }
+    }
+
+    pub fn with_alloc<'b, 'r, O>(&'r self, func: impl FnOnce(&'b ScopedAllocator<'a>) -> O) -> O
+    where
+        'a: 'b,
+        'r: 'b,
+    {
+        func(self)
+    }
+}
+
+unsafe impl Allocator for &ScopedAllocator<'_> {
+    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
+        let mut inner = self.inner.borrow_mut();
+        let memory = &mut inner.current;
+
+        let addr: NonNull<u8> = memory.cast();
+        let offset = addr.align_offset(layout.align());
+
+        if offset + layout.size() > memory.len() {
+            return Err(AllocError);
+        }
+
+        let allocated = unsafe {
+            // SAFETY: `addr + offset` won't overflow.
+            NonNull::slice_from_raw_parts(addr.add(offset), layout.size())
+        };
+
+        unsafe {
+            // SAFETY: `allocated + layout.size()` won't overflow.
+            *memory = NonNull::slice_from_raw_parts(
+                allocated.cast::<u8>().add(layout.size()),
+                memory.len() - offset - layout.size(),
+            );
+        }
+
+        inner.allocated_count += 1;
+        Ok(allocated)
+    }
+
+    unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {
+        self.inner.borrow_mut().allocated_count -= 1;
+    }
+}
+
+impl Drop for ScopedAllocator<'_> {
+    fn drop(&mut self) {
+        let inner = self.inner.borrow();
+        if inner.allocated_count > 0 {
+            panic!(
+                "Memory leak detected: {} allocations not deallocated",
+                inner.allocated_count
+            );
+        }
+    }
+}

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

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

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

@@ -0,0 +1,5 @@
+use eonix_hal_traits::trap::IsRawTrapContext;
+
+pub use crate::arch::trap::{disable_irqs, disable_irqs_save, enable_irqs, IrqState, TrapContext};
+
+struct _CheckTrapContext(IsRawTrapContext<TrapContext>);

+ 1 - 1
crates/eonix_mm/src/address.rs

@@ -7,7 +7,7 @@ mod vaddr;
 pub use addr::{Addr, AddrOps};
 pub use addr_range::AddrRange;
 pub use error::AddressError;
-pub use paddr::PAddr;
+pub use paddr::{PAddr, PhysAccess};
 pub use vaddr::VAddr;
 
 pub type PRange = AddrRange<PAddr>;

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

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

+ 24 - 0
crates/eonix_mm/src/address/paddr.rs

@@ -3,8 +3,32 @@ use crate::paging::{PAGE_SIZE_BITS, PFN};
 use core::{
     fmt,
     ops::{Add, Sub},
+    ptr::NonNull,
 };
 
+pub trait PhysAccess {
+    /// Translate the data that this address is pointing to into kernel
+    /// accessible pointer. Use it with care.
+    ///
+    /// # Panic
+    /// If the address is not properly aligned.
+    ///
+    /// # Safety
+    /// The caller must ensure that the data is of type `T`.
+    /// Otherwise, it may lead to undefined behavior.
+    unsafe fn as_ptr<T>(paddr: PAddr) -> NonNull<T>;
+
+    /// Translate the kernel accessible pointer back into a physical address.
+    ///
+    /// # Panic
+    /// If the pointer is not properly aligned.
+    ///
+    /// # Safety
+    /// The caller must ensure that the pointer is valid and points to a
+    /// valid physical memory location.
+    unsafe fn from_ptr<T>(ptr: NonNull<T>) -> PAddr;
+}
+
 #[repr(transparent)]
 #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
 pub struct PAddr(usize);

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

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

+ 54 - 22
crates/eonix_mm/src/page_table/page_table.rs

@@ -42,10 +42,27 @@ where
     A: PageAlloc,
     X: PageAccess,
 {
-    pub fn new_in<A1: PageAlloc>(kernel_root_table_page: &Page<A1>, alloc: A) -> Self {
+    pub fn with_root_table(root_table_page: Page<A>) -> Self {
+        Self {
+            root_table_page,
+            phantom: PhantomData,
+        }
+    }
+
+    pub fn clone_global<'b, B>(&self) -> PageTable<'b, M, B, X>
+    where
+        B: GlobalPageAlloc,
+    {
+        self.clone_in(B::global())
+    }
+
+    pub fn clone_in<'b, B>(&self, alloc: B) -> PageTable<'b, M, B, X>
+    where
+        B: PageAlloc,
+    {
         let new_root_table_page = Page::alloc_in(alloc);
         let new_table_data = X::get_ptr_for_page(&new_root_table_page);
-        let kernel_table_data = X::get_ptr_for_page(kernel_root_table_page);
+        let kernel_table_data = X::get_ptr_for_page(&self.root_table_page);
 
         unsafe {
             // SAFETY: `new_table_data` and `kernel_table_data` are both valid pointers
@@ -65,10 +82,7 @@ where
             root_page_table.index_mut(idx).take();
         }
 
-        Self {
-            root_table_page: new_root_table_page,
-            phantom: PhantomData,
-        }
+        PageTable::with_root_table(new_root_table_page)
     }
 
     pub fn addr(&self) -> PAddr {
@@ -87,7 +101,7 @@ where
     }
 
     pub fn iter_kernel(&self, range: VRange) -> impl Iterator<Item = &mut M::Entry> {
-        Self::iter_kernel_levels(self, range, M::LEVELS)
+        self.iter_kernel_levels(range, M::LEVELS)
     }
 
     /// Iterates over the kernel space entries in the page table for the specified levels.
@@ -115,17 +129,47 @@ where
         range: VRange,
         levels: &'static [PageTableLevel],
     ) -> impl Iterator<Item = &mut M::Entry> {
-        let alloc = self.root_table_page.allocator();
+        self.iter_kernel_in(range, levels, self.root_table_page.allocator())
+    }
+
+    /// Iterates over the kernel space entries in the page table for the specified levels
+    /// with a given page allocator.
+    ///
+    /// # Parameters
+    /// - `range`: The virtual address range to iterate over.
+    /// - `levels`: A slice of `PageTableLevel` that specifies which levels of the page table
+    ///   should be included in the iteration. Each level corresponds to a level in the page
+    ///   table hierarchy, and the iterator will traverse entries at these levels.
+    /// - `alloc`: A page allocator that provides memory for the page table entries.
+    ///
+    /// # Returns
+    /// An iterator over mutable references to the page table entries (`M::Entry`) within the
+    /// specified range and levels.
+    ///
+    /// # Example
+    /// ```no_run
+    /// let range = VRange::new(0x1234000, 0x1300000);
+    /// let levels = &M::LEVELS[..2];
+    /// for pte in page_table.iter_kernel_in(range, levels, NoAlloc) {
+    ///     // Process each entry
+    /// }
+    /// ```
+    pub fn iter_kernel_in<A1: PageAlloc>(
+        &self,
+        range: VRange,
+        levels: &'static [PageTableLevel],
+        alloc: A1,
+    ) -> impl Iterator<Item = &mut M::Entry> {
         let page_table_ptr = X::get_ptr_for_page(&self.root_table_page);
         let root_page_table = unsafe {
             // SAFETY: `page_table_ptr` is a valid pointer to a page table.
             M::RawTable::from_ptr(page_table_ptr)
         };
 
-        PageTableIterator::<M, A, X, KernelIterator>::with_levels(
+        PageTableIterator::<M, A1, X, KernelIterator>::with_levels(
             root_page_table,
             range,
-            alloc.clone(),
+            alloc,
             levels,
         )
     }
@@ -161,18 +205,6 @@ where
     }
 }
 
-impl<'a, M, A, X> PageTable<'a, M, A, X>
-where
-    M: PagingMode,
-    M::Entry: 'a,
-    A: GlobalPageAlloc,
-    X: PageAccess,
-{
-    pub fn new<A1: PageAlloc>(kernel_root_table_page: &Page<A1>) -> Self {
-        Self::new_in(kernel_root_table_page, A::global())
-    }
-}
-
 impl<'a, M, A, X> Drop for PageTable<'a, M, A, X>
 where
     M: PagingMode,

+ 1 - 5
crates/eonix_mm/src/page_table/paging_mode.rs

@@ -1,15 +1,11 @@
 use super::{RawPageTable, PTE};
-use crate::{
-    address::{Addr as _, VAddr},
-    paging::PFN,
-};
+use crate::address::{Addr as _, VAddr};
 
 pub trait PagingMode {
     type Entry: PTE;
     type RawTable<'a>: RawPageTable<'a, Entry = Self::Entry>;
 
     const LEVELS: &'static [PageTableLevel];
-    const KERNEL_ROOT_TABLE_PFN: PFN;
 }
 
 #[derive(Clone, Copy, PartialOrd, PartialEq)]

+ 4 - 13
crates/eonix_mm/src/page_table/pte.rs

@@ -23,10 +23,12 @@ bitflags! {
         const COPY_ON_WRITE = 256;
         const MAPPED = 512;
         const ANONYMOUS = 1024;
+        const HUGE = 2048;
     }
 }
 
-pub trait RawAttribute: Copy {
+#[doc(notable_trait)]
+pub trait RawAttribute: Copy + From<PageAttribute> + From<TableAttribute> {
     /// Create a new attribute representing a non-present page.
     fn null() -> Self;
 
@@ -43,20 +45,9 @@ pub trait RawAttribute: Copy {
     /// # Panic
     /// The implementor should panic if invalid combinations of flags are present.
     fn as_page_attr(self) -> Option<PageAttribute>;
-
-    /// Convert the attribute to a raw value.
-    ///
-    /// # Panic
-    /// The implementor should panic if invalid combinations of flags are present.
-    fn from_table_attr(table_attr: TableAttribute) -> Self;
-
-    /// Convert the attribute to a raw value.
-    ///
-    /// # Panic
-    /// The implementor should panic if invalid combinations of flags are present.
-    fn from_page_attr(page_attr: PageAttribute) -> Self;
 }
 
+#[doc(notable_trait)]
 pub trait PTE: Sized {
     type Attr: RawAttribute;
 

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

@@ -41,7 +41,7 @@ pub trait IteratorType<M: PagingMode> {
 
             pte.set(
                 page.into_raw(),
-                <M::Entry as PTE>::Attr::from_table_attr(Self::page_table_attributes()),
+                <M::Entry as PTE>::Attr::from(Self::page_table_attributes()),
             );
 
             unsafe {

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

@@ -4,6 +4,6 @@ mod pfn;
 mod raw_page;
 
 pub use page::{Page, PageAccess, PageBlock, PAGE_SIZE, PAGE_SIZE_BITS};
-pub use page_alloc::{GlobalPageAlloc, PageAlloc};
+pub use page_alloc::{GlobalPageAlloc, NoAlloc, PageAlloc};
 pub use pfn::PFN;
-pub use raw_page::RawPage;
+pub use raw_page::{RawPage, UnmanagedRawPage};

+ 17 - 3
crates/eonix_mm/src/paging/page.rs

@@ -1,5 +1,5 @@
 use super::{GlobalPageAlloc, PageAlloc, RawPage as _, PFN};
-use crate::address::{AddrRange, PAddr};
+use crate::address::{AddrRange, PAddr, PhysAccess};
 use core::{fmt, mem::ManuallyDrop, ptr::NonNull, sync::atomic::Ordering};
 
 pub const PAGE_SIZE: usize = 4096;
@@ -14,6 +14,7 @@ pub const PAGE_SIZE_BITS: u32 = PAGE_SIZE.trailing_zeros();
 pub struct PageBlock([u8; PAGE_SIZE]);
 
 /// A trait that provides the kernel access to the page.
+#[doc(notable_trait)]
 pub trait PageAccess {
     /// Returns a kernel-accessible pointer to the page referenced by the given
     /// physical frame number.
@@ -99,7 +100,7 @@ where
     where
         F: FnOnce(&Self) -> O,
     {
-        unsafe { Self::with_raw_in(pfn, func, A::global()) }
+        unsafe { Self::with_raw_in(pfn, A::global(), func) }
     }
 
     /// Do some work with the page without touching the reference count with the same
@@ -187,7 +188,7 @@ where
     ///
     /// # Safety
     /// Check `from_raw_in()` for the safety requirements.
-    pub unsafe fn with_raw_in<F, O>(pfn: PFN, func: F, alloc: A) -> O
+    pub unsafe fn with_raw_in<F, O>(pfn: PFN, alloc: A, func: F) -> O
     where
         F: FnOnce(&Self) -> O,
     {
@@ -304,3 +305,16 @@ impl<A: PageAlloc> fmt::Debug for Page<A> {
         )
     }
 }
+
+impl<T> PageAccess for T
+where
+    T: PhysAccess,
+{
+    unsafe fn get_ptr_for_pfn(pfn: PFN) -> NonNull<PageBlock> {
+        unsafe {
+            // SAFETY: The physical address of a existing page must be
+            //         aligned to the page size.
+            T::as_ptr(PAddr::from(pfn))
+        }
+    }
+}

+ 28 - 1
crates/eonix_mm/src/paging/page_alloc.rs

@@ -1,4 +1,4 @@
-use super::RawPage;
+use super::{raw_page::UnmanagedRawPage, RawPage};
 
 /// A trait for allocating and deallocating pages of memory.
 ///
@@ -6,6 +6,7 @@ use super::RawPage;
 /// behavior, meaning that the allocators are to be passed around by value and stored in
 /// managed data structures. This is because the allocator may be used to deallocate the
 /// pages it allocates.
+#[doc(notable_trait)]
 pub trait PageAlloc: Clone {
     type RawPage: RawPage;
 
@@ -37,11 +38,15 @@ pub trait PageAlloc: Clone {
 /// A trait for global page allocators.
 ///
 /// Global means that we can get an instance of the allocator from anywhere in the kernel.
+#[doc(notable_trait)]
 pub trait GlobalPageAlloc: PageAlloc + 'static {
     /// Get the global page allocator.
     fn global() -> Self;
 }
 
+#[derive(Clone)]
+pub struct NoAlloc;
+
 impl<'a, A> PageAlloc for &'a A
 where
     A: PageAlloc,
@@ -60,3 +65,25 @@ where
         (*self).has_management_over(raw_page)
     }
 }
+
+impl PageAlloc for NoAlloc {
+    type RawPage = UnmanagedRawPage;
+
+    fn alloc_order(&self, _: u32) -> Option<Self::RawPage> {
+        panic!("`NoAlloc` cannot allocate pages");
+    }
+
+    unsafe fn dealloc(&self, _: Self::RawPage) {
+        panic!("`NoAlloc` cannot free pages");
+    }
+
+    fn has_management_over(&self, _: Self::RawPage) -> bool {
+        true
+    }
+}
+
+impl GlobalPageAlloc for NoAlloc {
+    fn global() -> Self {
+        Self
+    }
+}

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

@@ -3,9 +3,50 @@ use core::sync::atomic::AtomicUsize;
 
 /// A `RawPage` represents a page of memory in the kernel. It is a low-level
 /// representation of a page that is used by the kernel to manage memory.
+#[doc(notable_trait)]
 pub trait RawPage: Clone + Copy + From<PFN> + Into<PFN> {
     fn order(&self) -> u32;
     fn refcount(&self) -> &AtomicUsize;
 
     fn is_present(&self) -> bool;
 }
+
+#[derive(Clone, Copy)]
+pub struct UnmanagedRawPage(PFN, u32);
+
+/// Unmanaged raw pages should always have a non-zero refcount to
+/// avoid `free()` from being called.
+static UNMANAGED_RAW_PAGE_CLONE_COUNT: AtomicUsize = AtomicUsize::new(1);
+
+impl UnmanagedRawPage {
+    pub const fn new(pfn: PFN, order: u32) -> Self {
+        Self(pfn, order)
+    }
+}
+
+impl From<PFN> for UnmanagedRawPage {
+    fn from(value: PFN) -> Self {
+        Self::new(value, 0)
+    }
+}
+
+impl Into<PFN> for UnmanagedRawPage {
+    fn into(self) -> PFN {
+        let Self(pfn, _) = self;
+        pfn
+    }
+}
+
+impl RawPage for UnmanagedRawPage {
+    fn order(&self) -> u32 {
+        self.1
+    }
+
+    fn refcount(&self) -> &AtomicUsize {
+        &UNMANAGED_RAW_PAGE_CLONE_COUNT
+    }
+
+    fn is_present(&self) -> bool {
+        true
+    }
+}

+ 1 - 6
crates/eonix_percpu/Cargo.toml

@@ -3,10 +3,5 @@ name = "eonix_percpu"
 version = "0.1.0"
 edition = "2024"
 
-[lib]
-proc-macro = true
-
 [dependencies]
-proc-macro2 = "1.0"
-quote = "1.0"
-syn = { version = "2.0", features = ["full"] }
+eonix_percpu_macros = { path = "./eonix_percpu_macros" }

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

@@ -0,0 +1,14 @@
+[package]
+name = "eonix_percpu_macros"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+arch_macros = { path = "../../../arch/arch_macros" }
+
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = { version = "2.0", features = ["full"] }

+ 42 - 13
arch/percpu-macros/src/lib.rs → crates/eonix_percpu/eonix_percpu_macros/src/lib.rs

@@ -1,18 +1,19 @@
 extern crate proc_macro;
 
-use proc_macro::TokenStream;
+use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
-use syn::{parse_macro_input, ItemStatic};
+use syn::{parse2, Ident, ItemStatic, Type};
 
-mod arch;
-
-#[proc_macro_attribute]
-pub fn define_percpu(attrs: TokenStream, item: TokenStream) -> TokenStream {
+fn define_percpu_impl(
+    attrs: TokenStream,
+    item: TokenStream,
+    get_percpu_pointer: fn(&Ident, &Type) -> TokenStream,
+) -> TokenStream {
     if !attrs.is_empty() {
         panic!("`define_percpu` attribute does not take any arguments");
     }
 
-    let item = parse_macro_input!(item as ItemStatic);
+    let item = parse2::<ItemStatic>(item).unwrap();
     let vis = &item.vis;
     let ident = &item.ident;
     let ty = &item.ty;
@@ -53,7 +54,7 @@ pub fn define_percpu(attrs: TokenStream, item: TokenStream) -> TokenStream {
         quote! {}
     };
 
-    let as_ptr = arch::get_percpu_pointer(&inner_ident, &ty);
+    let as_ptr = get_percpu_pointer(&inner_ident, &ty);
 
     quote! {
         #[link_section = ".percpu"]
@@ -116,13 +117,16 @@ pub fn define_percpu(attrs: TokenStream, item: TokenStream) -> TokenStream {
     .into()
 }
 
-#[proc_macro_attribute]
-pub fn define_percpu_shared(attrs: TokenStream, item: TokenStream) -> TokenStream {
+fn define_percpu_shared_impl(
+    attrs: TokenStream,
+    item: TokenStream,
+    get_percpu_pointer: fn(&Ident, &Type) -> TokenStream,
+) -> TokenStream {
     if !attrs.is_empty() {
         panic!("`define_percpu_shared` attribute does not take any arguments");
     }
 
-    let item = parse_macro_input!(item as ItemStatic);
+    let item = parse2::<ItemStatic>(item).unwrap();
     let vis = &item.vis;
     let ident = &item.ident;
     let ty = &item.ty;
@@ -131,7 +135,7 @@ pub fn define_percpu_shared(attrs: TokenStream, item: TokenStream) -> TokenStrea
     let inner_ident = format_ident!("_percpu_shared_inner_{}", ident);
     let access_ident = format_ident!("_access_shared_{}", ident);
 
-    let as_ptr = arch::get_percpu_pointer(&inner_ident, &ty);
+    let as_ptr = get_percpu_pointer(&inner_ident, &ty);
 
     quote! {
         #[link_section = ".percpu"]
@@ -153,7 +157,7 @@ pub fn define_percpu_shared(attrs: TokenStream, item: TokenStream) -> TokenStrea
 
             pub fn get_for_cpu(&self, cpuid: usize) -> Option<& #ty > {
                 let offset = & #inner_ident as *const _ as usize;
-                let base = ::arch::PercpuArea::get_for(cpuid);
+                let base = ::eonix_percpu::PercpuArea::get_for(cpuid);
                 base.map(|base| unsafe { base.byte_add(offset).cast().as_ref() })
             }
         }
@@ -177,5 +181,30 @@ pub fn define_percpu_shared(attrs: TokenStream, item: TokenStream) -> TokenStrea
             }
         }
     }
+}
+
+#[proc_macro_attribute]
+pub fn define_percpu_x86_64(
+    attrs: proc_macro::TokenStream,
+    item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    define_percpu_impl(
+        attrs.into(),
+        item.into(),
+        arch_macros::x86_64::percpu::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,
+    )
     .into()
 }

+ 0 - 24
crates/eonix_percpu/src/arch.rs

@@ -1,24 +0,0 @@
-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! {
-        {
-            #[cfg(target_arch = "x86_64")]
-            {
-                let base: *mut #ty;
-                ::core::arch::asm!(
-                    "mov %gs:0, {address}",
-                    "add ${percpu_pointer}, {address}",
-                    percpu_pointer = sym #percpu,
-                    address = out(reg) base,
-                    options(att_syntax)
-                );
-                base
-            }
-        }
-    }
-    .into()
-}

+ 58 - 160
crates/eonix_percpu/src/lib.rs

@@ -1,181 +1,79 @@
-extern crate proc_macro;
+#![no_std]
 
-use proc_macro::TokenStream;
-use quote::{format_ident, quote};
-use syn::{parse_macro_input, ItemStatic};
+use core::alloc::Layout;
+use core::ptr::null_mut;
+use core::ptr::NonNull;
+use core::sync::atomic::AtomicPtr;
+use core::sync::atomic::Ordering;
 
-mod arch;
+#[cfg(target_arch = "x86_64")]
+pub use eonix_percpu_macros::define_percpu_x86_64 as define_percpu;
 
-#[proc_macro_attribute]
-pub fn define_percpu(attrs: TokenStream, item: TokenStream) -> TokenStream {
-    if !attrs.is_empty() {
-        panic!("`define_percpu` attribute does not take any arguments");
-    }
-
-    let item = parse_macro_input!(item as ItemStatic);
-    let vis = &item.vis;
-    let ident = &item.ident;
-    let ty = &item.ty;
-    let expr = &item.expr;
+#[cfg(target_arch = "x86_64")]
+pub use eonix_percpu_macros::define_percpu_shared_x86_64 as define_percpu_shared;
 
-    let is_bool = quote!(#ty).to_string().as_str() == "bool";
-    let is_integer =
-        ["u8", "u16", "u32", "u64", "usize"].contains(&quote!(#ty).to_string().as_str());
+const MAX_CPUS: usize = 256;
 
-    let is_atomic_like = is_bool || is_integer || quote!(#ty).to_string().contains("NonNull");
+#[repr(align(16))]
+pub struct PercpuData();
 
-    let inner_ident = format_ident!("_percpu_inner_{}", ident);
-    let access_ident = format_ident!("_access_{}", ident);
+pub struct PercpuArea {
+    data: NonNull<PercpuData>,
+}
 
-    let integer_methods = if is_integer {
-        quote! {
-            pub fn add(&self, value: #ty) {
-                *unsafe { self.as_mut() } += value;
-            }
+static PERCPU_POINTERS: [AtomicPtr<PercpuData>; MAX_CPUS] =
+    [const { AtomicPtr::new(null_mut()) }; MAX_CPUS];
 
-            pub fn sub(&self, value: #ty) {
-                *unsafe { self.as_mut() } -= value;
-            }
+impl PercpuArea {
+    fn len() -> usize {
+        unsafe extern "C" {
+            fn PERCPU_LENGTH();
         }
-    } else {
-        quote! {}
-    };
-
-    let preempt_disable = if !is_atomic_like {
-        quote! { eonix_preempt::disable(); }
-    } else {
-        quote! {}
-    };
-
-    let preempt_enable = if !is_atomic_like {
-        quote! { eonix_preempt::enable(); }
-    } else {
-        quote! {}
-    };
-
-    let as_ptr = arch::get_percpu_pointer(&inner_ident, &ty);
-
-    quote! {
-        #[link_section = ".percpu"]
-        #[allow(non_upper_case_globals)]
-        static mut #inner_ident: #ty = #expr;
-        #[allow(non_camel_case_types)]
-        #vis struct #access_ident;
-        #vis static #ident: #access_ident = #access_ident;
-
-        impl #access_ident {
-            /// # Safety
-            /// This function is unsafe because it allows for mutable aliasing of the percpu
-            /// variable.
-            /// Make sure that preempt is disabled when calling this function.
-            pub unsafe fn as_ptr(&self) -> *mut #ty {
-                #as_ptr
-            }
-
-            pub fn get(&self) -> #ty {
-                #preempt_disable
-                let value = unsafe { self.as_ptr().read() };
-                #preempt_enable
-                value
-            }
-
-            pub fn set(&self, value: #ty) {
-                #preempt_disable
-                unsafe { self.as_ptr().write(value) }
-                #preempt_enable
-            }
-
-            pub fn swap(&self, mut value: #ty) -> #ty {
-                #preempt_disable
-                unsafe { self.as_ptr().swap(&mut value) }
-                #preempt_enable
-                value
-            }
-
-            /// # Safety
-            /// This function is unsafe because it allows for immutable aliasing of the percpu
-            /// variable.
-            /// Make sure that preempt is disabled when calling this function.
-            pub unsafe fn as_ref(&self) -> & #ty {
-                // SAFETY: This is safe because `as_ptr()` is guaranteed to be valid.
-                self.as_ptr().as_ref().unwrap()
-            }
-
-            /// # Safety
-            /// This function is unsafe because it allows for mutable aliasing of the percpu
-            /// variable.
-            /// Make sure that preempt is disabled when calling this function.
-            pub unsafe fn as_mut(&self) -> &mut #ty {
-                // SAFETY: This is safe because `as_ptr()` is guaranteed to be valid.
-                self.as_ptr().as_mut().unwrap()
-            }
-
-            #integer_methods
+        let len = PERCPU_LENGTH as usize;
+
+        assert_ne!(len, 0, "Percpu length should not be zero.");
+        len
+    }
+
+    fn data_start() -> NonNull<u8> {
+        unsafe extern "C" {
+            fn PERCPU_DATA_START();
         }
+
+        let addr = PERCPU_DATA_START as usize;
+        NonNull::new(addr as *mut _).expect("Percpu data should not be null.")
     }
-    .into()
-}
 
-#[proc_macro_attribute]
-pub fn define_percpu_shared(attrs: TokenStream, item: TokenStream) -> TokenStream {
-    if !attrs.is_empty() {
-        panic!("`define_percpu_shared` attribute does not take any arguments");
+    fn layout() -> Layout {
+        Layout::from_size_align(Self::len(), align_of::<PercpuData>()).expect("Invalid layout.")
     }
 
-    let item = parse_macro_input!(item as ItemStatic);
-    let vis = &item.vis;
-    let ident = &item.ident;
-    let ty = &item.ty;
-    let expr = &item.expr;
-
-    let inner_ident = format_ident!("_percpu_shared_inner_{}", ident);
-    let access_ident = format_ident!("_access_shared_{}", ident);
-
-    let as_ptr = arch::get_percpu_pointer(&inner_ident, &ty);
-
-    quote! {
-        #[link_section = ".percpu"]
-        #[allow(non_upper_case_globals)]
-        static #inner_ident: #ty = #expr;
-        #[allow(non_camel_case_types)]
-        #vis struct #access_ident;
-        #vis static #ident: #access_ident = #access_ident;
-
-        impl #access_ident {
-            fn as_ptr(&self) -> *const #ty {
-                unsafe { ( #as_ptr ) }
-            }
-
-            pub fn get_ref(&self) -> & #ty {
-                // SAFETY: This is safe because `as_ptr()` is guaranteed to be valid.
-                unsafe { self.as_ptr().as_ref().unwrap() }
-            }
-
-            pub fn get_for_cpu(&self, cpuid: usize) -> Option<& #ty > {
-                let offset = & #inner_ident as *const _ as usize;
-                let base = ::arch::PercpuArea::get_for(cpuid);
-                base.map(|base| unsafe { base.byte_add(offset).cast().as_ref() })
-            }
-        }
+    pub fn new<F>(allocate: F) -> Self
+    where
+        F: FnOnce(Layout) -> NonNull<u8>,
+    {
+        let data_pointer = allocate(Self::layout());
 
-        impl ::core::ops::Deref for #access_ident {
-            type Target = #ty;
+        unsafe {
+            // SAFETY: The `data_pointer` is of valid length and properly aligned.
+            data_pointer.copy_from_nonoverlapping(Self::data_start(), Self::len());
+        }
 
-            fn deref(&self) -> &Self::Target {
-                self.get_ref()
-            }
+        Self {
+            data: data_pointer.cast(),
         }
+    }
 
-        impl<T> ::core::convert::AsRef<T> for #access_ident
-        where
-            <Self as ::core::ops::Deref>::Target: ::core::convert::AsRef<T>,
-        {
-            fn as_ref(&self) -> &T {
-                use ::core::ops::Deref;
+    pub fn register(self, cpuid: usize) {
+        PERCPU_POINTERS[cpuid].store(self.data.as_ptr(), Ordering::Release);
+    }
 
-                self.deref().as_ref()
-            }
-        }
+    pub fn get_for(cpuid: usize) -> Option<NonNull<()>> {
+        let pointer = PERCPU_POINTERS[cpuid].load(Ordering::Acquire);
+        NonNull::new(pointer.cast())
+    }
+
+    pub fn setup(&mut self, func: impl FnOnce(NonNull<PercpuData>)) {
+        func(self.data)
     }
-    .into()
 }

+ 1 - 1
crates/eonix_preempt/Cargo.toml

@@ -4,4 +4,4 @@ version = "0.1.0"
 edition = "2024"
 
 [dependencies]
-arch = { path = "../../arch" }
+eonix_percpu = { path = "../eonix_percpu" }

+ 42 - 11
crates/eonix_preempt/src/lib.rs

@@ -1,8 +1,18 @@
 #![no_std]
 
-use core::sync::atomic::{compiler_fence, Ordering};
+use core::{
+    ops::{Deref, DerefMut},
+    sync::atomic::{compiler_fence, Ordering},
+};
 
-#[arch::define_percpu]
+pub struct PreemptGuard<T>
+where
+    T: ?Sized,
+{
+    value: T,
+}
+
+#[eonix_percpu::define_percpu]
 static PREEMPT_COUNT: usize = 0;
 
 #[inline(always)]
@@ -66,17 +76,38 @@ macro_rules! assert_preempt_count_eq {
     }};
 }
 
-#[unsafe(no_mangle)]
-pub extern "C" fn r_preempt_disable() {
-    disable();
+impl<T> PreemptGuard<T> {
+    pub fn new(value: T) -> Self {
+        disable();
+        Self { value }
+    }
+}
+
+impl<T> Deref for PreemptGuard<T>
+where
+    T: ?Sized,
+{
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.value
+    }
 }
 
-#[unsafe(no_mangle)]
-pub extern "C" fn r_preempt_enable() {
-    enable();
+impl<T> DerefMut for PreemptGuard<T>
+where
+    T: ?Sized,
+{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.value
+    }
 }
 
-#[unsafe(no_mangle)]
-pub extern "C" fn r_preempt_count() -> usize {
-    count()
+impl<T> Drop for PreemptGuard<T>
+where
+    T: ?Sized,
+{
+    fn drop(&mut self) {
+        enable();
+    }
 }

+ 2 - 0
crates/eonix_runtime/Cargo.toml

@@ -6,7 +6,9 @@ edition = "2024"
 [dependencies]
 arch = { path = "../../arch" }
 atomic_unique_refcell = { path = "../atomic_unique_refcell" }
+eonix_hal = { path = "../eonix_hal" }
 eonix_log = { path = "../eonix_log" }
+eonix_percpu = { path = "../eonix_percpu" }
 eonix_preempt = { path = "../eonix_preempt" }
 eonix_sync = { path = "../eonix_sync" }
 pointers = { path = "../pointers" }

+ 10 - 10
crates/eonix_runtime/src/context.rs

@@ -1,51 +1,51 @@
 use core::{cell::UnsafeCell, mem::transmute};
+use eonix_hal::context::TaskContext;
+use eonix_hal::traits::context::RawTaskContext;
 
 #[derive(Debug)]
-pub struct ExecutionContext(UnsafeCell<arch::TaskContext>);
+pub struct ExecutionContext(UnsafeCell<TaskContext>);
 
 unsafe impl Sync for ExecutionContext {}
 
 impl ExecutionContext {
     pub const fn new() -> Self {
-        Self(UnsafeCell::new(arch::TaskContext::new()))
+        Self(UnsafeCell::new(TaskContext::new()))
     }
 
     pub fn set_ip(&mut self, ip: usize) {
         let Self(context) = self;
-        context.get_mut().ip(ip);
+        context.get_mut().set_program_counter(ip);
     }
 
     pub fn set_sp(&mut self, sp: usize) {
         let Self(context) = self;
-        context.get_mut().sp(sp);
+        context.get_mut().set_stack_pointer(sp);
     }
 
     pub fn set_interrupt(&mut self, is_enabled: bool) {
         let Self(context) = self;
-        context.get_mut().interrupt(is_enabled);
+        context.get_mut().set_interrupt_enabled(is_enabled);
     }
 
     pub fn call1<T>(&mut self, func: unsafe extern "C" fn(T) -> !, arg: usize) {
         let Self(context) = self;
         context
             .get_mut()
-            .call1(unsafe { transmute(func as *mut ()) }, [arg]);
+            .call(unsafe { transmute(func as *mut ()) }, arg);
     }
 
     pub fn switch_to(&self, to: &Self) {
         let Self(from_ctx) = self;
         let Self(to_ctx) = to;
         unsafe {
-            arch::TaskContext::switch(&mut *from_ctx.get(), &mut *to_ctx.get());
+            TaskContext::switch(&mut *from_ctx.get(), &mut *to_ctx.get());
         }
     }
 
     pub fn switch_noreturn(&self) -> ! {
-        let mut from_ctx = arch::TaskContext::new();
         let Self(to_ctx) = self;
         unsafe {
-            arch::TaskContext::switch(&mut from_ctx, &mut *to_ctx.get());
+            TaskContext::switch_to_noreturn(&mut *to_ctx.get());
         }
-        unreachable!("We should never return from switch_to_noreturn");
     }
 }

+ 1 - 1
crates/eonix_runtime/src/ready_queue.rs

@@ -2,7 +2,7 @@ use crate::task::Task;
 use alloc::{collections::VecDeque, sync::Arc};
 use eonix_sync::Spin;
 
-#[arch::define_percpu_shared]
+#[eonix_percpu::define_percpu_shared]
 static READYQUEUE: Spin<FifoReadyQueue> = Spin::new(FifoReadyQueue::new());
 
 pub trait ReadyQueue {

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

@@ -18,10 +18,10 @@ use eonix_sync::{LazyLock, Spin, SpinIrq as _};
 use intrusive_collections::RBTree;
 use pointers::BorrowedArc;
 
-#[arch::define_percpu]
+#[eonix_percpu::define_percpu]
 static CURRENT_TASK: Option<NonNull<Task>> = None;
 
-#[arch::define_percpu]
+#[eonix_percpu::define_percpu]
 static LOCAL_SCHEDULER_CONTEXT: ExecutionContext = ExecutionContext::new();
 
 static TASKS: LazyLock<Spin<RBTree<TaskAdapter>>> =
@@ -92,6 +92,7 @@ impl Scheduler {
             let context: &mut ExecutionContext = LOCAL_SCHEDULER_CONTEXT.as_mut();
             context.set_ip(local_scheduler as _);
             context.set_sp(stack.get_bottom().addr().get() as usize);
+            context.set_interrupt(true);
             eonix_preempt::enable();
         }
 

+ 6 - 7
crates/eonix_sync/Cargo.toml

@@ -1,13 +1,12 @@
+[workspace]
+members = ["eonix_spin", "eonix_sync_rt", "eonix_sync_base"]
+
 [package]
 name = "eonix_sync"
 version = "0.1.0"
 edition = "2024"
 
 [dependencies]
-arch = { path = "../../arch" }
-eonix_preempt = { path = "../eonix_preempt" }
-intrusive-collections = "0.9.7"
-
-[features]
-default = []
-no_check_locked = []
+eonix_spin = { path = "./eonix_spin" }
+eonix_sync_base = { path = "./eonix_sync_base" }
+eonix_sync_rt = { path = "./eonix_sync_rt" }

+ 8 - 0
crates/eonix_sync/eonix_spin/Cargo.toml

@@ -0,0 +1,8 @@
+[package]
+name = "eonix_spin"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+eonix_preempt = { path = "../../eonix_preempt" }
+eonix_sync_base = { path = "../eonix_sync_base" }

+ 1 - 1
crates/eonix_sync/src/spin/guard.rs → crates/eonix_sync/eonix_spin/src/guard.rs

@@ -1,12 +1,12 @@
 use super::{
     ContextUnlock, DisablePreemption, Relax, Spin, SpinContext, SpinRelax, UnlockedContext,
 };
-use crate::{marker::NotSend, UnlockableGuard, UnlockedGuard};
 use core::{
     marker::PhantomData,
     mem::ManuallyDrop,
     ops::{Deref, DerefMut},
 };
+use eonix_sync_base::{NotSend, UnlockableGuard, UnlockedGuard};
 
 pub struct SpinGuard<'a, T, C = DisablePreemption, R = SpinRelax>
 where

+ 11 - 4
crates/eonix_sync/src/spin.rs → crates/eonix_sync/eonix_spin/src/lib.rs

@@ -1,16 +1,15 @@
+#![no_std]
+
 mod guard;
-mod relax;
-mod spin_irq;
 
 use core::{
     cell::UnsafeCell,
     marker::PhantomData,
     sync::atomic::{AtomicBool, Ordering},
 };
+use eonix_sync_base::{Relax, SpinRelax};
 
 pub use guard::{SpinGuard, UnlockedSpinGuard};
-pub use relax::{LoopRelax, Relax, SpinRelax};
-pub use spin_irq::SpinIrq;
 
 pub trait SpinContext {
     fn save() -> Self;
@@ -57,6 +56,14 @@ where
             _phantom: PhantomData,
         }
     }
+
+    pub fn into_inner(mut self) -> T {
+        assert!(
+            !*self.locked.get_mut(),
+            "Spin::take(): Cannot take a locked Spin"
+        );
+        self.value.into_inner()
+    }
 }
 
 impl<T, R> Spin<T, R>

+ 10 - 0
crates/eonix_sync/eonix_sync_base/Cargo.toml

@@ -0,0 +1,10 @@
+[package]
+name = "eonix_sync_base"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+
+[features]
+default = []
+no_check_locked = []

+ 0 - 0
crates/eonix_sync/src/guard.rs → crates/eonix_sync/eonix_sync_base/src/guard.rs


+ 0 - 0
crates/eonix_sync/src/lazy_lock.rs → crates/eonix_sync/eonix_sync_base/src/lazy_lock.rs


+ 13 - 0
crates/eonix_sync/eonix_sync_base/src/lib.rs

@@ -0,0 +1,13 @@
+#![no_std]
+
+mod guard;
+mod lazy_lock;
+mod locked;
+mod marker;
+mod relax;
+
+pub use guard::{UnlockableGuard, UnlockedGuard};
+pub use lazy_lock::LazyLock;
+pub use locked::{AsProof, AsProofMut, Locked, Proof, ProofMut};
+pub use marker::{NotSend, NotSync};
+pub use relax::{LoopRelax, Relax, SpinRelax};

+ 0 - 0
crates/eonix_sync/src/locked.rs → crates/eonix_sync/eonix_sync_base/src/locked.rs


+ 0 - 0
crates/eonix_sync/src/locked/proof.rs → crates/eonix_sync/eonix_sync_base/src/locked/proof.rs


+ 0 - 0
crates/eonix_sync/src/marker.rs → crates/eonix_sync/eonix_sync_base/src/marker.rs


+ 0 - 0
crates/eonix_sync/src/spin/relax.rs → crates/eonix_sync/eonix_sync_base/src/relax.rs


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

@@ -0,0 +1,13 @@
+[package]
+name = "eonix_sync_rt"
+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" }
+eonix_sync_base = { path = "../eonix_sync_base" }
+
+intrusive-collections = "0.9.7"

+ 11 - 0
crates/eonix_sync/eonix_sync_rt/src/lib.rs

@@ -0,0 +1,11 @@
+#![no_std]
+
+mod mutex;
+mod rwlock;
+mod spin_irq;
+mod wait_list;
+
+pub use mutex::{Mutex, MutexGuard};
+pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
+pub use spin_irq::SpinIrq;
+pub use wait_list::{WaitHandle, WaitList};

+ 0 - 0
crates/eonix_sync/src/mutex.rs → crates/eonix_sync/eonix_sync_rt/src/mutex.rs


+ 1 - 1
crates/eonix_sync/src/mutex/guard.rs → crates/eonix_sync/eonix_sync_rt/src/mutex/guard.rs

@@ -1,9 +1,9 @@
 use super::Mutex;
-use crate::{UnlockableGuard, UnlockedGuard};
 use core::{
     ops::{Deref, DerefMut},
     sync::atomic::Ordering,
 };
+use eonix_sync_base::{UnlockableGuard, UnlockedGuard};
 
 pub struct MutexGuard<'a, T>
 where

+ 0 - 0
crates/eonix_sync/src/rwlock.rs → crates/eonix_sync/eonix_sync_rt/src/rwlock.rs


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