356 Commits bad164e483 ... 755b006871

Tác giả SHA1 Thông báo Ngày
  greatbridf 755b006871 Merge pull request #58 from greatbridf/fix/termios-buffer-overflow 2 tháng trước cách đây
  greatbridf 2967de5de3 syscall: fix struct termios definitions 7 tháng trước cách đây
  greatbridf 2fc31b7eb3 Merge pull request #59 from greatbridf/fix/loongarch64-boot 2 tháng trước cách đây
  Heinz d962c61b0f fix(hal): fix loongarch's boot fault 7 tháng trước cách đây
  greatbridf affe0c764b Merge pull request #46 from greatbridf/task-rework 2 tháng trước cách đây
  greatbridf 8c656b5898 configure: check and use ARCH given in env 7 tháng trước cách đây
  greatbridf c57b71ff95 Merge remote-tracking branch 'SMS-Derfflinger/ext4-replace' into task-rework 7 tháng trước cách đây
  greatbridf 3fb4966118 task: fix infinite sleep in stackful tasks 7 tháng trước cách đây
  greatbridf 34a6252968 feat: unwinding and printing stack backtrace 7 tháng trước cách đây
  greatbridf db931a8038 partial work: file array rework and asynchronize 7 tháng trước cách đây
  greatbridf 973f6f2c71 partial work: vfs asynchronize 7 tháng trước cách đây
  greatbridf dee96a3a6a syscall: migrate all syscalls to async... 7 tháng trước cách đây
  greatbridf 21b765092f task: fix stackful waker implementation 7 tháng trước cách đây
  greatbridf a622172587 trap: introduce Breakpoint fault type 7 tháng trước cách đây
  greatbridf 30bfc5a0db loongarch64, trap: rework to fix nested captured traps 7 tháng trước cách đây
  greatbridf 9c900be225 task, thread: working version of threads 7 tháng trước cách đây
  greatbridf 661a15940b riscv64, trap: rework to fix nested captured traps 7 tháng trước cách đây
  greatbridf 874a4fa000 task: migrate all Task::block_on calls to task::block_on 7 tháng trước cách đây
  greatbridf 21dd5ea1c7 rcu: provide call_rcu() to call rcu drop asynchronously 7 tháng trước cách đây
  greatbridf 5ada0d0634 build, Makefile: remove --feature if none is present 7 tháng trước cách đây
  greatbridf 33ff3156a0 task: brand new block_on and stackful wrapper 7 tháng trước cách đây
  greatbridf 6b152c74dd riscv64, trap: fix kernel space trap returns 7 tháng trước cách đây
  greatbridf 3ab454f6df riscv64, trap: remove load_interrupt_stack impl 7 tháng trước cách đây
  greatbridf fb9a175e70 runtime: add trace logs and fix few bugs 7 tháng trước cách đây
  greatbridf e23c9eb1f2 runtime: new task sleep-wakeup method and some adaption 7 tháng trước cách đây
  greatbridf e89a286104 runtime: rework the whole runtime arch. (partial) 8 tháng trước cách đây
  Heinz a2c50b9a11 feat(fs): temporary cache write back strategy for ext4 8 tháng trước cách đây
  Heinz db1caebde5 feat(fs): partial work for ext4's page cache 8 tháng trước cách đây
  Heinz 082c5c52b4 Merge branch 'master' into ext4-replace 8 tháng trước cách đây
  greatbridf 4bb09f5ef9 Merge pull request #44 from greatbridf/pipeline 8 tháng trước cách đây
  greatbridf 9a207f58ec pipeline: disable kvm during the test 8 tháng trước cách đây
  greatbridf c0fcd375bd build, Makefile: check for kvm presence before using it 8 tháng trước cách đây
  greatbridf 35be7f9868 pipeline, Makefile: override the qemu provided in env 8 tháng trước cách đây
  greatbridf 621d8996ed pipeline: run some script to test functionalities 8 tháng trước cách đây
  greatbridf c8b721d605 pipeline: add test build pipeline 8 tháng trước cách đây
  greatbridf 9e7e047578 Merge pull request #43 from greatbridf/comp-fix-syscall 8 tháng trước cách đây
  greatbridf 5efa0f49f6 Merge pull request #39 from greatbridf/loongarch64 8 tháng trước cách đây
  Heinz 806c4fe0ac fix(fs): fix ext4's write offset update 8 tháng trước cách đây
  greatbridf b3ee4067e3 build-img.sh: use sudo only if detected and valid 8 tháng trước cách đây
  greatbridf d52f87d1f9 build: add an option to specify output filename in configure script 8 tháng trước cách đây
  greatbridf b321265202 mm, arch: refine page cache impl and count cpu on la64 8 tháng trước cách đây
  greatbridf 3329f1bfa8 task: call platform shutdown on panicking for la64 8 tháng trước cách đây
  greatbridf f0dd8ec22c syscall: fix wrong pselect6 syscall no for la64 8 tháng trước cách đây
  greatbridf 287bb545c5 arch, la: setup timer and timer interrupt on bootstrap 8 tháng trước cách đây
  greatbridf 40ca79574d arch: shutdown support for loongarch64 virt platforms 8 tháng trước cách đây
  Heinz 2d61f60c0c syscall: more new syscall impls 9 tháng trước cách đây
  greatbridf 5c9d03601a style(riscv64): remove stale TODO message 8 tháng trước cách đây
  greatbridf 333f3907d4 Merge branch 'master' into loongarch64 8 tháng trước cách đây
  greatbridf 6c6195b183 Merge pull request #36 from Shao-ZW/pagecache 8 tháng trước cách đây
  greatbridf c5b5572fc6 chore: change some typo and format 8 tháng trước cách đây
  greatbridf 1e785ffa92 Merge branch 'master' into pagecache 8 tháng trước cách đây
  Heinz 22458ed33c fix(fs): fix rename's metadata 8 tháng trước cách đây
  Heinz 1d1a0257ba feat(fs): impl rename 8 tháng trước cách đây
  Heinz 5c4016615a fix(fs): fix some informations 8 tháng trước cách đây
  Heinz d59a550880 feat(fs): impl remove file and dir. 8 tháng trước cách đây
  Heinz f05037374c feat(fs): impl write, create and mkdir for ext4 fs 8 tháng trước cách đây
  Heinz f6c26c956c perf: replace the annoyed ext4 crate with a new choice (called another ext4 crate 8 tháng trước cách đây
  greatbridf fdc65f0d30 feat: bug fix and some temporary solutions 8 tháng trước cách đây
  greatbridf 84dca27600 feat(page cache): refactor PageCache to remove size parameter and add size method in backend 8 tháng trước cách đây
  greatbridf 7da2b94cc2 Merge remote-tracking branch 'upstream/master' into pagecache 8 tháng trước cách đây
  greatbridf 3cae0870ae Merge pull request #37 from Shao-ZW/master 8 tháng trước cách đây
  Zhuowei Shao 698ca6c5d7 Merge pull request #38 from greatbridf/fix-execve 9 tháng trước cách đây
  greatbridf d9e144bbb0 change(syscall): add tgkill and rename some syscall definitions 9 tháng trước cách đây
  greatbridf 87345b756a fix(execve): clear the registers after we've done the execve call 9 tháng trước cách đây
  greatbridf 651149202a fix(execve): clear the registers after we've done the execve call 9 tháng trước cách đây
  greatbridf af00747a1a feat(arch): working impl of loongarch64 9 tháng trước cách đây
  greatbridf 97574ce60b feat(arch): add loongarch64 bootstrap code 9 tháng trước cách đây
  zhuowei shao ede54cb224 feat(wait): refactor waitid handling and introduce WaitId enum 9 tháng trước cách đây
  Heinz 6d9027b71b Merge pull request #35 from greatbridf/interpreter 9 tháng trước cách đây
  greatbridf ae41558771 Merge pull request #26 from SMS-Derfflinger/riscv64-smp 9 tháng trước cách đây
  zhuowei shao fe995aa528 feat: implement shared memory syscalls and mmap syscall for file mapping 9 tháng trước cách đây
  zhuowei shao 409b0633e1 feat(page fault): refactor mmap page fault handle to deal with page cache and sharing mapping 9 tháng trước cách đây
  zhuowei shao af512033d5 feat: implement page cache for efficient vfs io 9 tháng trước cách đây
  Heinz 21f6feb418 change(hal): change ap's temporary boot stack to only one stack 9 tháng trước cách đây
  greatbridf 66f509c5d6 change(smp): use FDT.harts() in riscv64 bootstrap_smp 9 tháng trước cách đây
  greatbridf 80731802bd fix(ap_start): replace mul with left shifts in riscv64 ap entry 9 tháng trước cách đây
  Heinz c264c7ee6c Merge branch 'master' into riscv64-smp 9 tháng trước cách đây
  Heinz 792633d3e8 style(hal): remove unused import and unused function 9 tháng trước cách đây
  greatbridf 34bb98b1f1 feat(syscall): impl get_random and getegid 9 tháng trước cách đây
  greatbridf 14ca94cd85 change: use the script directly in kernel init 9 tháng trước cách đây
  greatbridf 61da3f6689 style: remove unused imports 9 tháng trước cách đây
  greatbridf 56f11a3175 feat(execve): support the shebang scripts 9 tháng trước cách đây
  greatbridf de301086a1 style: remove unused imports 9 tháng trước cách đây
  Zhuowei Shao f07a81c34a Merge pull request #33 from greatbridf/fix 9 tháng trước cách đây
  greatbridf 835a1ef4d2 fix(ext4): use new Instant and get_name methods 9 tháng trước cách đây
  greatbridf 3219293b2f Merge pull request #31 from greatbridf/fix-syscall 9 tháng trước cách đây
  greatbridf dd114cb94d Merge pull request #20 from SMS-Derfflinger/ext4-support 9 tháng trước cách đây
  greatbridf 50f4ea5700 doc(readme): update configure and Makefile usage 9 tháng trước cách đây
  greatbridf 39b892a434 chore(configure): update the Makefile and configure script to provide testcase image path 9 tháng trước cách đây
  greatbridf d9c5949512 change(init_script): check whether the ext4 image exists before mounting the image 9 tháng trước cách đây
  greatbridf 65b3b66088 change(ext4): rework some part of the ext4 filesystem 9 tháng trước cách đây
  greatbridf d1e32f1ba8 style: remove some unused imports and pubs 9 tháng trước cách đây
  greatbridf 532b88c4c3 Merge pull request #25 from Shao-ZW/robust-list 9 tháng trước cách đây
  greatbridf 1751a0cb89 Merge branch 'master' into robust-list 9 tháng trước cách đây
  greatbridf 990e1c6ad8 fix(set_robust_list): postpone the reading of robust_list_head until we exit 9 tháng trước cách đây
  greatbridf 378a70188a fix(syscall): newfstat should be called fstat64 for x86 targets 9 tháng trước cách đây
  greatbridf a6b8a3c8c3 fix(fat32): validate the cluster no, treating 0, 1 as EOC 9 tháng trước cách đây
  greatbridf 3026229113 fix(elf): add AT_RANDOM auxiliary vector entry 9 tháng trước cách đây
  greatbridf 8f42972027 feat(syscall): add newfstat syscall 9 tháng trước cách đây
  greatbridf 2b85bbc015 fix(brk): return fail if the given break pos is less than current 9 tháng trước cách đây
  Heinz 03fe491ec6 remove riscv64 cpu's bootstrap function 9 tháng trước cách đây
  Heinz 2a25dbda3b feat(hal): improve riscv64 smp, get ap start addr from label 9 tháng trước cách đây
  greatbridf 7178806d53 feat(syscall): impl renameat 9 tháng trước cách đây
  Heinz 0359279932 feat(hal): impl smp bootstrap for riscv64 9 tháng trước cách đây
  Heinz c0ffa04589 fix(block): improve block's partprobe to register whole disk partition, and sort fdt's device node by start address 9 tháng trước cách đây
  greatbridf 937e5a47c1 Merge pull request #19 from greatbridf/fix-syscall 9 tháng trước cách đây
  zhuowei shao bf6c04837b feat: implement syscall get_robust_list 9 tháng trước cách đây
  Heinz 0d98e9bbe6 Merge branch 'master' into ext4-support 9 tháng trước cách đây
  Heinz faff005b55 fix(fs): fix ext4 fs in riscv64, It will work normally only when a single ext4 image is mounted. 9 tháng trước cách đây
  greatbridf b212bc8ca2 feat(vfs): update {a, m, c}time accordingly when we modify the inodes 9 tháng trước cách đây
  greatbridf 5fdaaef28e feat(rtc): implement RTC for riscv64 9 tháng trước cách đây
  greatbridf b1fb9dea12 fix(timer): riscv64 should have timer frequency 1000Hz 9 tháng trước cách đây
  greatbridf e356f5143b feat(syscall): impl fcntl::F_{GET, SET}FL 9 tháng trước cách đây
  Heinz de242d19af fix(fs): fix ext4 fs, now x86_64 can read test image normally 9 tháng trước cách đây
  greatbridf bdc6dfb5a3 fix(exit): don't panic when we have user access fault on exiting 9 tháng trước cách đây
  greatbridf 2c152fdca0 fix(execve): return error code instead of panicking when interpreter can't be found 9 tháng trước cách đây
  greatbridf d6dcfee4d2 fix(FixEntry): make riscv64 fixup list entry correctly aligned and laid out 9 tháng trước cách đây
  greatbridf 82260f8ed8 feat: impl several more syscalls, bump linux version to 5.17 9 tháng trước cách đây
  greatbridf 481948d2cb Merge pull request #18 from greatbridf/dev-drivers 9 tháng trước cách đây
  greatbridf 98ac37c635 feat(syscall): impl fch{own, mod}at and utimensat 9 tháng trước cách đây
  greatbridf ef950102c0 change(linker): enlarge the RAM size for riscv64 targets to support release mode builds 9 tháng trước cách đây
  greatbridf 1513e66772 fix(FileArray): release the spinlock before dropping inner files 9 tháng trước cách đây
  greatbridf 576c43db2d fix(CoW): check both PRESENT and MAPPED bits when doing cow 9 tháng trước cách đây
  greatbridf ca174932df fix(syscall): `fstatat` should place device number major at bit 8..16 9 tháng trước cách đây
  greatbridf 42b95ebe83 feat(syscall): impl lseek for riscv64 9 tháng trước cách đây
  greatbridf dad0fa00ef feat(interrupt): add external interrupt support for riscv64 9 tháng trước cách đây
  greatbridf 9932b08358 change(trap): improve trap handling process 9 tháng trước cách đây
  greatbridf 6dd58675ae feat(serial): add serial driver support for riscv64 9 tháng trước cách đây
  Heinz 77e6bd8f0c feat(fs): add ext4 fs support based on ext4_rs crate 9 tháng trước cách đây
  greatbridf 1e9ae1be41 feat(BlockDevice): add write requests for block devices 9 tháng trước cách đây
  greatbridf 16cb0de6bd Merge branch 'master' into dev-drivers 9 tháng trước cách đây
  greatbridf e5d5464c96 Merge pull request #9 from SMS-Derfflinger/riscv64-support 9 tháng trước cách đây
  Heinz f7d4f9d574 style(hal): remove unused import and modify annotation 9 tháng trước cách đây
  Heinz e027b453ca fix(hal): fix riscv enable/disable_irq 9 tháng trước cách đây
  Heinz 7280c46efb feat(hal): add timer interrupt enable 9 tháng trước cách đây
  greatbridf fae9380b40 feat: add syscall ppoll, fix clock_gettime for riscv64 9 tháng trước cách đây
  greatbridf c64b5660fa fix(x86_64): as_table_attr should return None when encounter non-table page attributes 9 tháng trước cách đây
  greatbridf c2eab5d866 feat: impl vdso for all architectures and sigreturn for riscv64 9 tháng trước cách đây
  greatbridf ac9a6b2f1f Merge remote-tracking branch 'upstream/master' into riscv64-support 9 tháng trước cách đây
  Heinz 8160bbd621 Merge pull request #10 from Shao-ZW/master 9 tháng trước cách đây
  greatbridf 9f15decb66 change(mm_list): rewrite `protect` with new syntax 9 tháng trước cách đây
  greatbridf da6298e357 change(clone): parse clone args at entry point and save them into Option's 9 tháng trước cách đây
  greatbridf c9fc06cae9 change(Process): remove `do_kill_process` and use `do_exit` for those use cases 9 tháng trước cách đây
  zhuowei shao 11304dd3c3 feat: add pthread test 9 tháng trước cách đây
  greatbridf 485546e8d0 feat: add Stream used for write operations 9 tháng trước cách đây
  zhuowei shao 5dd3a57da4 feat: implement futex 9 tháng trước cách đây
  zhuowei shao b72c76b3f7 fix: fix mmap 9 tháng trước cách đây
  zhuowei shao 5e3b6ec384 fix: fix exit 9 tháng trước cách đây
  zhuowei shao 5bcdb8040b feat: implement clone 9 tháng trước cách đây
  zhuowei shao a8df9df74b fix: fix mprotect 9 tháng trước cách đây
  greatbridf 4d5acf492a style: remove unused imports 9 tháng trước cách đây
  greatbridf dbd48ae587 chore(build): rework of Makefile to provide better support of multiarch compilation 9 tháng trước cách đây
  greatbridf dd1d5927e1 feat: impl openat and argument printing in trace_syscall 9 tháng trước cách đây
  greatbridf a0b22bb416 style: remove arch crate 9 tháng trước cách đây
  greatbridf 353fe877ea feat: add compilation option for riscv64, fix x86 compile 9 tháng trước cách đây
  greatbridf 73a10e4727 feat(syscall): impl fstatat 9 tháng trước cách đây
  greatbridf 1667f0f905 feat(UserBuffer): working user buffer in riscv64 cpus 9 tháng trước cách đây
  greatbridf c19e26e0d1 fix(trap): update sp in kernel space traps 9 tháng trước cách đây
  greatbridf ec45d646ef feat: syscall adaption 9 tháng trước cách đây
  greatbridf c68b315120 fix: `trap_return` and `captured_trap_handler` should save `from_ctx` 9 tháng trước cách đây
  greatbridf 963a3b1bb9 Merge remote-tracking branch 'upstream/master' into riscv64-support 9 tháng trước cách đây
  greatbridf 026229c8cb Merge remote-tracking branch 'origin/riscv64-support' into riscv64-support 9 tháng trước cách đây
  greatbridf 942f24e842 Merge pull request #8 from Shao-ZW/master 9 tháng trước cách đây
  greatbridf ae9becdc7b Update script/build-img.sh 9 tháng trước cách đây
  greatbridf 9cf926f974 fix: intermediate page tables should not set A, D and U bits 9 tháng trước cách đây
  greatbridf 2d868ba813 partial work: working trap 9 tháng trước cách đây
  greatbridf 070dcd8779 fix(set_root_page_table): satp::set should take pfn 9 tháng trước cách đây
  greatbridf bb2b276c8e partial work: impl virtio block device and sbi console 9 tháng trước cách đây
  greatbridf 4351cf5573 partial work: fix riscv64 bootstrap 9 tháng trước cách đây
  Heinz 38fd7687b8 revert: revert .cargo and user dataflow 9 tháng trước cách đây
  Heinz 7dd53d9544 delete arch/src/riscv64's old code 9 tháng trước cách đây
  greatbridf c1b395f508 feat(timer): add sleep function 9 tháng trước cách đây
  Heinz 0c6a045342 change link.x.in and disable some function for debug 9 tháng trước cách đây
  Heinz cb3a72ca15 fix(arch): fix bad addi instructions, replace by la 9 tháng trước cách đây
  Heinz 1bd97d4db6 Merge branch 'master' into riscv64-support 9 tháng trước cách đây
  Heinz a62b1b1d96 temporarily set dataflow to empty 9 tháng trước cách đây
  Heinz 56a357bdd0 change hal riscv64 config and fdt implementation 9 tháng trước cách đây
  Heinz 191877a3ac feat(hal): impl basic single hart bootstrap for riscv64 9 tháng trước cách đây
  Heinz 89366051d1 feat(hal): impl trap handler for riscv64 9 tháng trước cách đây
  Heinz 8b683d90b8 change arch riscv64's some function to nop 9 tháng trước cách đây
  Heinz 4ea007b2ed fix(arch): replace mhart read by sbi passed parameter 9 tháng trước cách đây
  Heinz 8325f02e78 feat(hal): add empty TLS for riscv64 9 tháng trước cách đây
  Heinz 35d94146e9 feat(hal): impl basic cpu for riscv64 9 tháng trước cách đây
  Heinz 42998d1d85 feat(hal): impl RawTaskContext for riscv64 9 tháng trước cách đây
  Heinz 58ef3c435f feat(hal): impl riscv64's some instructions and clear arch/ 9 tháng trước cách đây
  Heinz a890455ca4 feat(hal): impl riscv64's fpu 9 tháng trước cách đây
  Heinz ef171c4792 feat(hal): impl riscv64's interrupt data structure 9 tháng trước cách đây
  zhuowei shao 0eac6e12b5 refactor(loader): unify ELF32 and ELF64 loading 9 tháng trước cách đây
  Heinz 36801071f3 feat(hal): add riscv64's fdt 9 tháng trước cách đây
  Heinz bf50c85f90 feat(hal): add riscv64's trap context and some assembly instructions 9 tháng trước cách đây
  Heinz 601c879103 feat(hal): add riscv64's Sv39 page mode 9 tháng trước cách đây
  Heinz 384477d57e feat(hal): add riscv64's linker scrpit 9 tháng trước cách đây
  Heinz c0f4799102 feat(hal): impl percpu for riscv64 9 tháng trước cách đây
  zhuowei shao 1ff75b9e8f feat(loader): rewrite loader and support dynamic loading 9 tháng trước cách đây
  Zhuowei Shao 0189d83b46 Merge pull request #7 from greatbridf/makefile 9 tháng trước cách đây
  greatbridf ccd06b426d chore(makefile): add an option to control the kernel features 10 tháng trước cách đây
  Heinz c7e982c2bf Merge branch 'master' into riscv64-support 10 tháng trước cách đây
  greatbridf 49e32c3462 feat(e1000e): adapt e1000e driver to the new kernel 10 tháng trước cách đây
  greatbridf 8a5a58271e feat(AHCI): add AHCI port write command 10 tháng trước cách đây
  greatbridf 2b8042afc2 Merge pull request #6 from greatbridf/remove-cpp 10 tháng trước cách đây
  greatbridf 96295d86c9 fix(buddy): erroneous condition check on alloc initialization 10 tháng trước cách đây
  greatbridf dd32b93963 feat(hal): smp initialization 10 tháng trước cách đây
  Heinz 998febde0c refactor(arch): refactor riscv64's bootstrap, already be tested 10 tháng trước cách đây
  Heinz d92ae935f8 change arch riscv64 start stage 10 tháng trước cách đây
  greatbridf 8672c72a5d feat(hal): working impl of HAL. 10 tháng trước cách đây
  greatbridf fb605cede1 change(arch): move {Task, Trap}Context to HAL crate 10 tháng trước cách đây
  Heinz 6e2e032016 feat(arch): impl eonix hal trap trait for riscv64 10 tháng trước cách đây
  Heinz c33e229a78 Merge branch 'master' into riscv64-support 10 tháng trước cách đây
  Heinz bea9c15b7f feat(arch): impl save restore for FpuRegisters 10 tháng trước cách đây
  Heinz 477364e5d8 feat(arch): build up trap framework 10 tháng trước cách đây
  greatbridf 72eb66819c remove(bindgen): completely move bindgen 10 tháng trước cách đây
  greatbridf 262b76ad95 remove(bindgen): move all the used constants to `kernel::constants` 10 tháng trước cách đây
  greatbridf 7ca12b658a style(arch): add pub to suppress unused warnings 10 tháng trước cách đây
  greatbridf 02b24cbf0b change(arch): remove old `InterruptContext` 10 tháng trước cách đây
  greatbridf 47087f162e change(c++): remove c++ stl and boot.s 10 tháng trước cách đây
  greatbridf 1cc6683440 fix(sendfile): `sendfile` returns wrong written byte count 10 tháng trước cách đây
  greatbridf 24008a54a2 Merge branch 'master' into remove-cpp 10 tháng trước cách đây
  greatbridf 43016845e4 partial work of making the new trap handling method work 10 tháng trước cách đây
  greatbridf f4e68bd903 Merge pull request #5 from Shao-ZW:master 10 tháng trước cách đây
  greatbridf 862471ca82 style: change visibility of internal raw page fields 10 tháng trước cách đây
  greatbridf b8c5c2be51 change(allocator): use Page for huge object allocation 10 tháng trước cách đây
  greatbridf 400662db4e style: remove `Clone` constraint in `SlabAllocator`. 10 tháng trước cách đây
  Heinz 42b4d5c683 fix(arch): fix riscv64 CPU num from fdt parse 10 tháng trước cách đây
  Heinz a6e19d87ec fix(arch): fix riscv64 bootstrap's error caused by page attribute 10 tháng trước cách đây
  greatbridf 33d30d3413 partial work: make all syscalls use the new interface 10 tháng trước cách đây
  Heinz d6506ec044 feat(arch): add fdt parse for riscv64 10 tháng trước cách đây
  Heinz 9bcd7f8095 change arch riscv64 start stage 10 tháng trước cách đây
  Heinz 61dd83dec7 Merge branch 'master' into riscv64-support 10 tháng trước cách đây
  Heinz 56083a0e82 feat(arch): impl CPU data structure for riscv64. 10 tháng trước cách đây
  Heinz d0c35b8e01 feat(arch): impl interrupt's data structure 10 tháng trước cách đây
  greatbridf 87bf20d2cc partial work: define eonix_hal::default_trap_handler 10 tháng trước cách đây
  greatbridf 00252343e9 partial work of hal interface 10 tháng trước cách đây
  zhuowei shao ef8434b70a refactor: better slab trait interface 10 tháng trước cách đây
  zhuowei shao dd3080f63e refactor: refactor slab allocator 10 tháng trước cách đây
  greatbridf f1f1f6ac24 Merge branch 'trap-abstraction' into remove-cpp 10 tháng trước cách đây
  greatbridf eb7d52a441 Merge branch 'master' into remove-cpp 10 tháng trước cách đây
  Heinz 90af40f15f Merge pull request #4 from greatbridf/debug-support 10 tháng trước cách đây
  greatbridf 35f9f1c0d7 chore(ide): add vscode debug script 10 tháng trước cách đây
  greatbridf 4691d469c5 refactor(pcie): rewrite pcie bus driver in rust 10 tháng trước cách đây
  greatbridf 35d750f16f Merge pull request #3 from greatbridf/remove-cpp 10 tháng trước cách đây
  greatbridf ba4318f467 chore(build.rs): fix build script generation for Release targets 10 tháng trước cách đây
  greatbridf ebd3d1224c change(x86): optimize bootstrap code, remove kinit.cpp 10 tháng trước cách đây
  greatbridf ec187ff5df feat(spin): separate the procedure of saving lock context from spinlocks 10 tháng trước cách đây
  Heinz b5cee82465 feat(arch): add fence for rv64 10 tháng trước cách đây
  Heinz adfb1a5244 Merge branch 'master' into riscv64-support 10 tháng trước cách đây
  Heinz 530df496c2 feat(arch): add console io and mmio io 10 tháng trước cách đây
  greatbridf 630621a376 Merge pull request #2 from SMS-Derfflinger/master 10 tháng trước cách đây
  greatbridf 39828e5e2a style(mm): remove unused imports 10 tháng trước cách đây
  greatbridf 383393294b change(mm): add `page_size()` to `PageTableLevel`, some style changes 10 tháng trước cách đây
  greatbridf 894cfb4efc Rename new_levels to with_levels 10 tháng trước cách đây
  Heinz 8e05b3965b change(mm): replace enum with &[PageTableLevel] 10 tháng trước cách đây
  Heinz 6e412c736d change arch riscv64 with mm 10 tháng trước cách đây
  Heinz 21067db6c5 change(mm): replace enum with &[PageTableLevel] 10 tháng trước cách đây
  Heinz b4b8564055 change(mm) 10 tháng trước cách đây
  Heinz 1fa2d6f94d refactor(mm): add level choose in kernel iterator 10 tháng trước cách đây
  Heinz c42ce68abb change with mm 10 tháng trước cách đây
  Heinz 641eb7d8da change(mm) 10 tháng trước cách đây
  Heinz b364127dac refactor(arch): refactor riscv64's bootstrap 10 tháng trước cách đây
  Heinz ab5112942b refactor(mm): add level choose in kernel iterator 10 tháng trước cách đây
  greatbridf dc329bca82 percpu: make a better abstraction for trap handling 10 tháng trước cách đây
  Heinz 0625f57729 feat(arch): add task context for riscv64 10 tháng trước cách đây
  Heinz 8e31ffd7d3 refactor(arch): refactor riscv64's mm implementation 10 tháng trước cách đây
  Heinz 7ef7866d51 Merge branch 'master' into riscv64-support 10 tháng trước cách đây
  Heinz c960187e00 feat(arch): add entry for riscv64 10 tháng trước cách đây
  greatbridf 896553fd7b Merge branch 'mm-refactor' 10 tháng trước cách đây
  greatbridf c488664697 change(pte): new definitions for PTE attributes 10 tháng trước cách đây
  greatbridf 5dfe746c27 change(buddy): remove `Spin` from buddy allocator 10 tháng trước cách đây
  greatbridf cafb8b6bf8 fix(page_alloc): expose global buddy alloc to `mod cpu` 10 tháng trước cách đây
  greatbridf 676089587c change(page_alloc): better abstraction 10 tháng trước cách đây
  greatbridf b4e2516cbc mm: remove `take()` from `PTE` 10 tháng trước cách đây
  greatbridf 87f8f7b5b5 Merge pull request #1 from SMS-Derfflinger/master 10 tháng trước cách đây
  Heinz 799e7cbf38 refactor(mm): remove anonymous flag 10 tháng trước cách đây
  Heinz 627958d4a0 feat(arch): impl mm for riscv64 10 tháng trước cách đây
  Heinz 7e20f8f748 refactor(mm): remove anonymous flag 10 tháng trước cách đây
  Heinz 4e6869156f feat(arch): impl mm for riscv64 10 tháng trước cách đây
  Heinz 6bb8ca94db Merge branch 'master' into riscv64-support 10 tháng trước cách đây
  Heinz 5ef1cefcb7 feat: add some riscv64 bootstrap code 10 tháng trước cách đây
  greatbridf c2f64394c6 Merge branch 'mm-refactor' 10 tháng trước cách đây
  greatbridf dc97e018fa refactor(mm): new abstraction of page table and etc. 10 tháng trước cách đây
  Heinz fb564762d1 refactor: refactor bootstrap file structure 10 tháng trước cách đây
  Heinz bdf63e9d28 chore: add make and cmake mutil arch support 10 tháng trước cách đây
  Heinz 61b031b0a2 chore: add configure mutil arch support 10 tháng trước cách đây
  greatbridf 2a1aa49688 task: disable preemption in `unpark()` 11 tháng trước cách đây
  greatbridf 16acfb40f9 signal: separate signal.rs into several files 11 tháng trước cách đây
  greatbridf 3179e41a7c wait_list: add `get_waker_function` used in `SignalList::signal_waker` 11 tháng trước cách đây
  greatbridf a2ec93f2e3 temporary solution: use `Task::block_on` everywhere 11 tháng trước cách đây
  greatbridf c5a6a24ea8 eonix_sync: make sleeping functions `async` 11 tháng trước cách đây
  greatbridf b58b00ec34 wait_list: make sure the node is removed before dropping `Prepare` 11 tháng trước cách đây
  greatbridf 246d1e76d7 page_alloc: disable preemption while accessing PERCPU_PAGES 11 tháng trước cách đây
  greatbridf fe2b07f8b5 task: fix park and unpark memory order 11 tháng trước cách đây
  greatbridf 689a147b35 wait_list: improved waitlist arch 11 tháng trước cách đây
  greatbridf c1c82197b2 task: new task state management method 11 tháng trước cách đây
  greatbridf 05dc79b1b5 percpu: change the definition of `define_percpu_shared` 11 tháng trước cách đây
  greatbridf 03d2c0d298 percpu: add `percpu_shared` 11 tháng trước cách đây
  greatbridf f3c8032637 lazy_lock: add `get_mut` method. 11 tháng trước cách đây
  greatbridf 5b315d7831 lazy_lock: replace old lazy_static 11 tháng trước cách đây
  greatbridf 25266c1a21 spin: add doc for `Spin` 1 năm trước cách đây
  greatbridf 40e475897c rwlock: use `try_read_weak` in `read_slow_path` 1 năm trước cách đây
  greatbridf 3d55507589 mutex: refactor to get rid of `Lock` 1 năm trước cách đây
  greatbridf d52fc5232b arcswap: fix the wrong memory order 1 năm trước cách đây
  greatbridf 1dc9996b90 rwlock: refactor to get rid of `Lock` 1 năm trước cách đây
  greatbridf df68448e87 rwlock: fix lost wakeup problem 1 năm trước cách đây
  greatbridf 360dd9da38 mm_list: remove lock_irq restriction on `MMListInner` 1 năm trước cách đây
  greatbridf 147558c1d2 Merge branch 'spin-rework' into sync-extraction 1 năm trước cách đây
  greatbridf 3009c12592 spin: rewrite spinlock, add traits for guards 1 năm trước cách đây
  greatbridf 525eee2872 rwlock: rename rwsem to rwlock and reimpl the lock 1 năm trước cách đây
  greatbridf fa1c3af64e preempt: move r_preempt_{disable, enable, count} to eonix_preempt 1 năm trước cách đây
  greatbridf 8384960da7 locked: move Locked to enoix_sync 1 năm trước cách đây
  greatbridf a396b8add4 Merge branch 'sched-rewrite' into shiai-master 1 năm trước cách đây
  greatbridf 9e65c1c74a configure: remove the check for mtools 1 năm trước cách đây
  greatbridf 779805a7b1 refactor: separate parts into eonix_{log, preempt, sync, runtime} crates 1 năm trước cách đây
  greatbridf b29d448a4f serial: improve irq handle. add worker support 1 năm trước cách đây
  greatbridf f8ded5c5f2 task: add JoinHandle::join to wait for result 1 năm trước cách đây
  greatbridf ba9a93014f Merge branch 'sched-rewrite' into shiai-master 1 năm trước cách đây
  greatbridf a24eaa45e1 task: rewrite the task system to decouple better. 1 năm trước cách đây
  greatbridf c6f71ffe15 feat: add percpu readyqueue 1 năm trước cách đây
  greatbridf 16b686be83 chore: enable rust std pretty printers 1 năm trước cách đây
  greatbridf d0c3761bb3 fix: compilation error on x86 soft float targets 1 năm trước cách đây
  greatbridf 35db57a901 interrupt: check cs == 0x3 instead of ss == 0 in fault handlers 1 năm trước cách đây
  greatbridf 67a4bb90ea feat: skip core and alloc functions in debugger 1 năm trước cách đây
  shao bcb515cacd chore(build-img): fix Linux mount problem 1 năm trước cách đây
  greatbridf ca2ad4c427 Merge remote-tracking branch 'github/mm' into shiai-master 1 năm trước cách đây
  shao ab64258c5e feat(pcp): improve per-cpus pages support 1 năm trước cách đây
  greatbridf a3f7ad83a1 Merge remote-tracking branch 'github/mm' into shiai-master 1 năm trước cách đây
  shao 4e7bed8818 feat(pcp): add per-cpu pages support 1 năm trước cách đây
  greatbridf 6f90bfbd38 Merge branch 'fix' into shiai-master 1 năm trước cách đây
  greatbridf f10251a691 feat: add sys_chmod, remove debug prints 1 năm trước cách đây
  greatbridf ac2cb64f02 feat: blank impl for getrusage and madvise 1 năm trước cách đây
  greatbridf e9ec2880b8 feat: temp. impl for sys_times, update sys_statx 1 năm trước cách đây
  greatbridf ee488bcb18 feat(syscall): temporary impl of getrlimit, vfork and sysinfo 1 năm trước cách đây
  greatbridf eaa6cda7db style: remove unused warnings 1 năm trước cách đây
  greatbridf f5b4da3309 change(mm_list): separate mm_list from process on elf loading 1 năm trước cách đây
  greatbridf 555b383d90 feat(syscall): add sys_pipe2 1 năm trước cách đây
  greatbridf a66b415fd7 feat(syscall): add sys_prlimit64 1 năm trước cách đây
  greatbridf 780114025c clean: remove some redundant types 1 năm trước cách đây
  greatbridf d5421e4f17 feat(fat32): support of long file names 1 năm trước cách đây
  greatbridf c561df82d2 chore: add build-img script for making images 1 năm trước cách đây
  greatbridf 63a2aca6fb feat: add temporary impl for prlimit64 1 năm trước cách đây
  greatbridf 255b3636d7 fix(getcwd): do not copy to user directly 1 năm trước cách đây
  greatbridf 321fbcac00 fix(ahci): minus 1 from PRDTEntry.len 1 năm trước cách đây
  greatbridf 46beec348b style(block): change idents 1 năm trước cách đây
  greatbridf d400fb8fdc fix(setpgid): move the process to new pgroup 1 năm trước cách đây
  greatbridf 17e60b5661 doc: update origin 1 năm trước cách đây
  greatbridf 113ce48d38 doc: rename kernel name 1 năm trước cách đây
  shao 833e7f9c75 docs: add doc about memory management and multi-arch support 1 năm trước cách đây
  greatbridf 8a40ff8f6f doc: add more testcases 1 năm trước cách đây
  greatbridf 4c21fc6989 doc: update device_driver.md 1 năm trước cách đây
  greatbridf 3c76def031 style(block): change idents 1 năm trước cách đây
  greatbridf f7441a1470 doc: add device_driver.md 1 năm trước cách đây
  greatbridf e8b3f94f91 doc: add doc for filesystem 1 năm trước cách đây
  greatbridf 92cfd5ccee doc: add problems and TODO part 1 năm trước cách đây
  greatbridf c2a8b82a50 doc: add task.md 1 năm trước cách đây
  greatbridf 42eb4fbb73 chore: remove kvm accel for dists such as ubuntu 1 năm trước cách đây
  greatbridf aabe4b75b7 fix(setpgid): move the process to new pgroup 1 năm trước cách đây
  greatbridf e2c3fbd379 chore: update README.md, add docs 1 năm trước cách đây
  greatbridf afc4956f30 chore: rename project name 1 năm trước cách đây
100 tập tin đã thay đổi với 9468 bổ sung1937 xóa
  1. 2 1
      .cargo/config.toml
  2. 0 29
      .clang-format
  3. 66 0
      .github/workflows/test-build.yaml
  4. 1 3
      .gitignore
  5. 96 0
      .vscode/launch.json
  6. 3 0
      .vscode/settings.json
  7. 119 0
      .vscode/tasks.json
  8. 0 106
      CMakeLists.txt
  9. 352 181
      Cargo.lock
  10. 60 16
      Cargo.toml
  11. 180 35
      Makefile.src
  12. 193 41
      README.md
  13. 0 68
      arch/Cargo.lock
  14. 0 8
      arch/Cargo.toml
  15. 0 54
      arch/percpu-macros/Cargo.lock
  16. 0 21
      arch/percpu-macros/src/arch.rs
  17. 0 117
      arch/percpu-macros/src/lib.rs
  18. 0 14
      arch/src/lib.rs
  19. 0 71
      arch/src/x86_64/context.rs
  20. 0 241
      arch/src/x86_64/init.rs
  21. 0 480
      arch/src/x86_64/interrupt.rs
  22. 0 0
      arch/src/x86_64/interrupt.s
  23. 0 93
      arch/src/x86_64/io.rs
  24. 0 134
      arch/src/x86_64/mod.rs
  25. 0 16
      arch/src/x86_64/percpu.rs
  26. 0 54
      arch/src/x86_64/user.rs
  27. 0 13
      bochs.conf
  28. 8 26
      build.rs
  29. 75 92
      configure
  30. 7 0
      crates/atomic_unique_refcell/Cargo.lock
  31. 10 0
      crates/atomic_unique_refcell/Cargo.toml
  32. 104 0
      crates/atomic_unique_refcell/src/lib.rs
  33. 129 0
      crates/buddy_allocator/Cargo.lock
  34. 8 0
      crates/buddy_allocator/Cargo.toml
  35. 59 0
      crates/buddy_allocator/src/free_area.rs
  36. 97 0
      crates/buddy_allocator/src/lib.rs
  37. 146 0
      crates/buddy_allocator/src/zone.rs
  38. 29 0
      crates/eonix_hal/Cargo.toml
  39. 79 0
      crates/eonix_hal/build.rs
  40. 12 0
      crates/eonix_hal/eonix_hal_macros/Cargo.toml
  41. 165 0
      crates/eonix_hal/eonix_hal_macros/src/lib.rs
  42. 8 0
      crates/eonix_hal/eonix_hal_traits/Cargo.toml
  43. 41 0
      crates/eonix_hal/eonix_hal_traits/src/context.rs
  44. 23 0
      crates/eonix_hal/eonix_hal_traits/src/fault.rs
  45. 6 0
      crates/eonix_hal/eonix_hal_traits/src/fpu.rs
  46. 9 0
      crates/eonix_hal/eonix_hal_traits/src/lib.rs
  47. 6 0
      crates/eonix_hal/eonix_hal_traits/src/mm.rs
  48. 7 0
      crates/eonix_hal/eonix_hal_traits/src/processor.rs
  49. 115 0
      crates/eonix_hal/eonix_hal_traits/src/trap.rs
  50. 306 0
      crates/eonix_hal/src/arch/loongarch64/bootstrap.rs
  51. 110 0
      crates/eonix_hal/src/arch/loongarch64/context.rs
  52. 72 0
      crates/eonix_hal/src/arch/loongarch64/cpu.rs
  53. 32 0
      crates/eonix_hal/src/arch/loongarch64/fdt.rs
  54. 58 0
      crates/eonix_hal/src/arch/loongarch64/fence.rs
  55. 141 0
      crates/eonix_hal/src/arch/loongarch64/fpu.rs
  56. 97 0
      crates/eonix_hal/src/arch/loongarch64/link.x
  57. 21 0
      crates/eonix_hal/src/arch/loongarch64/memory.x
  58. 318 0
      crates/eonix_hal/src/arch/loongarch64/mm.rs
  59. 8 0
      crates/eonix_hal/src/arch/loongarch64/mod.rs
  60. 357 0
      crates/eonix_hal/src/arch/loongarch64/trap/mod.rs
  61. 308 0
      crates/eonix_hal/src/arch/loongarch64/trap/trap_context.rs
  62. 18 0
      crates/eonix_hal/src/arch/mod.rs
  63. 393 0
      crates/eonix_hal/src/arch/riscv64/bootstrap.rs
  64. 51 0
      crates/eonix_hal/src/arch/riscv64/config.rs
  65. 16 0
      crates/eonix_hal/src/arch/riscv64/console.rs
  66. 111 0
      crates/eonix_hal/src/arch/riscv64/context.rs
  67. 93 0
      crates/eonix_hal/src/arch/riscv64/cpu.rs
  68. 62 0
      crates/eonix_hal/src/arch/riscv64/fdt.rs
  69. 58 0
      crates/eonix_hal/src/arch/riscv64/fence.rs
  70. 111 0
      crates/eonix_hal/src/arch/riscv64/fpu.rs
  71. 152 0
      crates/eonix_hal/src/arch/riscv64/interrupt/mod.rs
  72. 93 0
      crates/eonix_hal/src/arch/riscv64/link.x
  73. 21 0
      crates/eonix_hal/src/arch/riscv64/memory.x
  74. 349 0
      crates/eonix_hal/src/arch/riscv64/mm.rs
  75. 12 0
      crates/eonix_hal/src/arch/riscv64/mod.rs
  76. 18 0
      crates/eonix_hal/src/arch/riscv64/time.rs
  77. 177 0
      crates/eonix_hal/src/arch/riscv64/trap/captured.rs
  78. 134 0
      crates/eonix_hal/src/arch/riscv64/trap/default.rs
  79. 115 0
      crates/eonix_hal/src/arch/riscv64/trap/mod.rs
  80. 302 0
      crates/eonix_hal/src/arch/riscv64/trap/trap_context.rs
  81. 516 0
      crates/eonix_hal/src/arch/x86_64/bootstrap.rs
  82. 361 0
      crates/eonix_hal/src/arch/x86_64/bootstrap/init.rs
  83. 113 0
      crates/eonix_hal/src/arch/x86_64/context.rs
  84. 225 0
      crates/eonix_hal/src/arch/x86_64/cpu.rs
  85. 46 0
      crates/eonix_hal/src/arch/x86_64/fence.rs
  86. 32 0
      crates/eonix_hal/src/arch/x86_64/fpu.rs
  87. 28 23
      crates/eonix_hal/src/arch/x86_64/gdt.rs
  88. 241 0
      crates/eonix_hal/src/arch/x86_64/interrupt.rs
  89. 37 0
      crates/eonix_hal/src/arch/x86_64/io.rs
  90. 109 0
      crates/eonix_hal/src/arch/x86_64/link.x
  91. 18 0
      crates/eonix_hal/src/arch/x86_64/memory.x
  92. 422 0
      crates/eonix_hal/src/arch/x86_64/mm.rs
  93. 10 0
      crates/eonix_hal/src/arch/x86_64/mod.rs
  94. 392 0
      crates/eonix_hal/src/arch/x86_64/trap.rs
  95. 199 0
      crates/eonix_hal/src/arch/x86_64/trap/trap_context.rs
  96. 32 0
      crates/eonix_hal/src/bootstrap.rs
  97. 1 0
      crates/eonix_hal/src/context.rs
  98. 45 0
      crates/eonix_hal/src/lib.rs
  99. 111 0
      crates/eonix_hal/src/link.x.in
  100. 201 0
      crates/eonix_hal/src/mm.rs

+ 2 - 1
.cargo/config.toml

@@ -1,6 +1,7 @@
 [build]
-target = 'x86_64-unknown-none.json'
+target = "riscv64gc-unknown-none-elf"
 target-dir = 'build'
+rustflags = ["-C", "force-unwind-tables"]
 
 [unstable]
 build-std-features = ['compiler-builtins-mem']

+ 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'
-
-...

+ 66 - 0
.github/workflows/test-build.yaml

@@ -0,0 +1,66 @@
+name: Test Build
+
+on:
+  push:
+    branches-ignore:
+      - comp-and-judge
+  pull_request:
+    branches-ignore:
+      - comp-and-judge
+  workflow_dispatch:
+
+jobs:
+  build-and-run-test:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        arch: [riscv64, loongarch64]
+        include:
+          - arch: riscv64
+            target: riscv64gc-unknown-none-elf
+          - arch: loongarch64
+            target: loongarch64-unknown-none-softfloat
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Setup rust nightly
+        run: rustup component add rust-src llvm-tools
+
+      - name: Setup QEMU
+        run: |
+          sudo apt-get install -y qemu-system-${{ matrix.arch }} qemu-kvm
+
+      - name: Configure
+        run: ./configure
+
+      - name: Run build for ${{ matrix.arch }} targets
+        run: |
+          make build ARCH=${{ matrix.arch }} MODE=release
+          zstd -k build/boot-${{ matrix.arch }}.img
+
+      - name: Upload build artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: eonix-kernel-and-image-${{ matrix.arch }}
+          path: |
+            build/${{ matrix.target }}/release/eonix_kernel
+            build/boot-${{ matrix.arch }}.img.zst
+
+      - name: Test run for ${{ matrix.arch }}
+        run: |
+          echo "Fixing permissions for /dev/kvm..."
+          sudo adduser $USER kvm
+          sh script/test.sh
+        env:
+          ARCH: ${{ matrix.arch }}
+          QEMU_ACCEL: ''
+        timeout-minutes: 2
+        continue-on-error: true
+
+      - name: Upload run log
+        uses: actions/upload-artifact@v4
+        with:
+          name: test-${{ matrix.arch }}.log
+          path: build/test-*.log

+ 1 - 3
.gitignore

@@ -1,6 +1,6 @@
 build/
 
-.vscode/
+.idea/
 
 test/
 
@@ -19,5 +19,3 @@ cross-compile.cmake
 .Trashes
 
 .gdbinit
-
-src/bindings.rs

+ 96 - 0
.vscode/launch.json

@@ -0,0 +1,96 @@
+{
+    "configurations": [
+        {
+            "type": "cppdbg",
+            "request": "launch",
+            "name": "Launch Kernel (riscv64)",
+            "program": "${workspaceFolder}/build/kernel.sym",
+            "args": [],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "miDebuggerPath": "/Users/david/.local/riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-apple-darwin/bin/riscv64-unknown-elf-gdb",
+            "miDebuggerServerAddress": "127.0.0.1:1234",
+            "setupCommands": [
+                {
+                    "text": "-enable-pretty-printing",
+                    "description": "Enable GDB pretty printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "text": "-exec set output-radix 16",
+                    "description": "Print addresses in hexadecimal",
+                    "ignoreFailures": true
+                },
+                // {
+                //     "text": "source ${workspaceFolder}/pretty-print.py",
+                //     "description": "Load GDB pretty printers",
+                //     "ignoreFailures": false
+                // },
+            ],
+            "preLaunchTask": "debug run riscv64",
+            "postDebugTask": "kill qemu riscv64"
+        },
+        {
+            "type": "cppdbg",
+            "request": "launch",
+            "name": "Launch Kernel (x86_64)",
+            "program": "${workspaceFolder}/build/kernel.sym",
+            "args": [],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "miDebuggerPath": "x86_64-elf-gdb",
+            "miDebuggerServerAddress": "127.0.0.1:1234",
+            "setupCommands": [
+                {
+                    "text": "-enable-pretty-printing",
+                    "description": "Enable GDB pretty printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "text": "-exec set output-radix 16",
+                    "description": "Print addresses in hexadecimal",
+                    "ignoreFailures": true
+                },
+                // {
+                //     "text": "source ${workspaceFolder}/pretty-print.py",
+                //     "description": "Load GDB pretty printers",
+                //     "ignoreFailures": false
+                // },
+            ],
+            "preLaunchTask": "debug run x86_64",
+            "postDebugTask": "kill qemu x86_64"
+        },
+        {
+            "type": "cppdbg",
+            "request": "launch",
+            "name": "Attach Kernel (x86_64)",
+            "program": "${workspaceFolder}/build/kernel.sym",
+            "args": [],
+            "stopAtEntry": false,
+            "cwd": "${workspaceFolder}",
+            "environment": [],
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "miDebuggerPath": "x86_64-elf-gdb",
+            "miDebuggerServerAddress": "127.0.0.1:1234",
+            "setupCommands": [
+                {
+                    "text": "-enable-pretty-printing",
+                    "description": "Enable GDB pretty printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "text": "-exec set output-radix 16",
+                    "description": "Print addresses in hexadecimal",
+                    "ignoreFailures": true
+                },
+            ]
+        }
+    ]
+}

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    "makefile.configureOnOpen": false,
+}

+ 119 - 0
.vscode/tasks.json

@@ -0,0 +1,119 @@
+{
+    // See https://go.microsoft.com/fwlink/?LinkId=733558
+    // for the documentation about the tasks.json format
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "debug run riscv64",
+            "type": "shell",
+            "command": "make srun ARCH=riscv64",
+            "isBackground": true,
+            "problemMatcher": [
+                {
+                    "owner": "rustc",
+                    "fileLocation": [
+                        "relative",
+                        "${workspaceFolder}"
+                    ],
+                    "pattern": {
+                        "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
+                        "file": 1,
+                        "line": 2,
+                        "column": 3,
+                        "severity": 4,
+                        "message": 5
+                    },
+                    "background": {
+                        "activeOnStart": true,
+                        "beginsPattern": "cargo build",
+                        "endsPattern": "qemu"
+                    }
+                }
+            ],
+            "presentation": {
+                "echo": false,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": false,
+                "clear": true
+            },
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
+        },
+        {
+            "label": "kill qemu riscv64",
+            "type": "shell",
+            "command": "killall qemu-system-riscv64",
+            "presentation": {
+                "echo": false,
+                "reveal": "never",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": false,
+                "clear": true
+            },
+            "group": {
+                "kind": "none",
+            }
+        },
+        {
+            "label": "debug run x86_64",
+            "type": "shell",
+            "command": "make srun ARCH=x86_64",
+            "isBackground": true,
+            "problemMatcher": [
+                {
+                    "owner": "rustc",
+                    "fileLocation": [
+                        "relative",
+                        "${workspaceFolder}"
+                    ],
+                    "pattern": {
+                        "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
+                        "file": 1,
+                        "line": 2,
+                        "column": 3,
+                        "severity": 4,
+                        "message": 5
+                    },
+                    "background": {
+                        "activeOnStart": true,
+                        "beginsPattern": "cargo build",
+                        "endsPattern": "qemu"
+                    }
+                }
+            ],
+            "presentation": {
+                "echo": false,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": false,
+                "clear": true
+            },
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
+        },
+        {
+            "label": "kill qemu x86_64",
+            "type": "shell",
+            "command": "killall qemu-system-x86_64",
+            "presentation": {
+                "echo": false,
+                "reveal": "never",
+                "focus": false,
+                "panel": "shared",
+                "showReuseMessage": false,
+                "clear": true
+            },
+            "group": {
+                "kind": "none",
+            }
+        }
+    ]
+}

+ 0 - 106
CMakeLists.txt

@@ -1,106 +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 dd if=mbr_hole.bin of=boot.img
-    COMMAND dd if=/dev/zero of=boot.img bs=`expr 512 \\* 1024 \\* 1024` count=0 seek=1
-    COMMAND sh -c \"echo n\; echo\; echo \; echo 8192\; echo\; echo a\; echo w\" | ${FDISK_BIN} boot.img
-    COMMAND mkfs.fat --offset=8192 -v -n SYSTEM boot.img
-    COMMAND mcopy -i boot.img@@4M ${CMAKE_BINARY_DIR}/user-space-program/hello-world.out ::hello
-    COMMAND mcopy -i boot.img@@4M ${CMAKE_BINARY_DIR}/user-space-program/interrupt-test.out ::int
-    COMMAND mcopy -i boot.img@@4M ${CMAKE_BINARY_DIR}/user-space-program/stack-test.out ::stack
-    COMMAND mcopy -i boot.img@@4M ${CMAKE_BINARY_DIR}/user-space-program/init.out ::init
-    COMMAND mcopy -i boot.img@@4M ${CMAKE_BINARY_DIR}/user-space-program/priv-test.out ::priv
-    COMMAND mcopy -i boot.img@@4M ${CMAKE_SOURCE_DIR}/busybox-minimal ::busybox_
-    COMMAND mcopy -i boot.img@@4M ${CMAKE_SOURCE_DIR}/busybox ::busybox
-    COMMAND mcopy -i boot.img@@4M ${CMAKE_SOURCE_DIR}/init_script.sh ::initsh
-)
-
-add_custom_command(OUTPUT run
-    POST_BUILD
-    DEPENDS boot.img
-    COMMAND bochs -f ${CMAKE_SOURCE_DIR}/bochs.conf
-)

+ 352 - 181
Cargo.lock

@@ -3,274 +3,430 @@
 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]]
-name = "arch"
+name = "align_ext"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1c330e503236d0b06386ae6cc42a513ef1ccc23c52b603c1b52f018564faf44"
+
+[[package]]
+name = "another_ext4"
 version = "0.1.0"
+source = "git+https://github.com/SMS-Derfflinger/another_ext4?branch=main#ed6d91718db721eb4a744483c289cc44a6f34bf4"
 dependencies = [
- "cfg-if",
- "percpu-macros",
+ "bitflags",
+ "log",
 ]
 
+[[package]]
+name = "atomic_unique_refcell"
+version = "0.1.0"
+
 [[package]]
 name = "autocfg"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 
 [[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"
-version = "2.6.0"
+version = "2.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
 
 [[package]]
-name = "cexpr"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+name = "buddy_allocator"
+version = "0.1.0"
 dependencies = [
- "nom",
+ "eonix_mm",
+ "intrusive_list",
 ]
 
 [[package]]
 name = "cfg-if"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+
+[[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "embedded-hal"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
+
+[[package]]
+name = "embedded-io"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
 
 [[package]]
-name = "clang-sys"
-version = "1.8.1"
+name = "enumn"
+version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
 dependencies = [
- "glob",
- "libc",
- "libloading",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
-name = "either"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+name = "eonix_hal"
+version = "0.1.0"
+dependencies = [
+ "acpi",
+ "bitflags",
+ "buddy_allocator",
+ "cfg-if",
+ "eonix_hal_macros",
+ "eonix_hal_traits",
+ "eonix_mm",
+ "eonix_percpu",
+ "eonix_preempt",
+ "eonix_sync_base",
+ "fdt",
+ "intrusive_list",
+ "loongArch64",
+ "riscv",
+ "sbi",
+]
+
+[[package]]
+name = "eonix_hal_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "eonix_hal_traits"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "eonix_mm",
+]
 
 [[package]]
-name = "gbos-rust-part"
+name = "eonix_kernel"
 version = "0.1.0"
 dependencies = [
- "arch",
- "bindgen",
+ "acpi",
+ "align_ext",
+ "another_ext4",
+ "atomic_unique_refcell",
  "bitflags",
+ "buddy_allocator",
+ "eonix_hal",
+ "eonix_log",
+ "eonix_macros",
+ "eonix_mm",
+ "eonix_percpu",
+ "eonix_preempt",
+ "eonix_runtime",
+ "eonix_sync",
+ "intrusive-collections 0.9.8",
+ "intrusive_list",
  "itertools",
- "lazy_static",
- "spin",
+ "pointers",
+ "posix_types",
+ "slab_allocator",
+ "stalloc",
+ "unwinding",
+ "virtio-drivers",
+ "xmas-elf",
 ]
 
 [[package]]
-name = "glob"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+name = "eonix_log"
+version = "0.1.0"
+dependencies = [
+ "eonix_sync",
+]
 
 [[package]]
-name = "itertools"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+name = "eonix_macros"
+version = "0.1.0"
 dependencies = [
- "either",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
-name = "lazy_static"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+name = "eonix_mm"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "eonix_percpu"
+version = "0.1.0"
+dependencies = [
+ "eonix_percpu_macros",
+]
+
+[[package]]
+name = "eonix_percpu_macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "eonix_preempt"
+version = "0.1.0"
 dependencies = [
- "spin",
+ "eonix_percpu",
 ]
 
 [[package]]
-name = "libc"
-version = "0.2.164"
+name = "eonix_runtime"
+version = "0.1.0"
+dependencies = [
+ "atomic_unique_refcell",
+ "eonix_hal",
+ "eonix_log",
+ "eonix_percpu",
+ "eonix_preempt",
+ "eonix_sync",
+ "intrusive-collections 0.9.7",
+ "pointers",
+]
+
+[[package]]
+name = "eonix_spin"
+version = "0.1.0"
+dependencies = [
+ "eonix_preempt",
+ "eonix_sync_base",
+]
+
+[[package]]
+name = "eonix_sync"
+version = "0.1.0"
+dependencies = [
+ "eonix_spin",
+ "eonix_sync_base",
+ "eonix_sync_rt",
+]
+
+[[package]]
+name = "eonix_sync_base"
+version = "0.1.0"
+
+[[package]]
+name = "eonix_sync_rt"
+version = "0.1.0"
+dependencies = [
+ "eonix_hal",
+ "eonix_preempt",
+ "eonix_spin",
+ "eonix_sync_base",
+ "intrusive-collections 0.9.7",
+]
+
+[[package]]
+name = "fdt"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
+
+[[package]]
+name = "gimli"
+version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
+checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe"
 
 [[package]]
-name = "libloading"
-version = "0.8.5"
+name = "intrusive-collections"
+version = "0.9.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
+checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86"
 dependencies = [
- "cfg-if",
- "windows-targets",
+ "memoffset",
 ]
 
 [[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+name = "intrusive-collections"
+version = "0.9.8"
+source = "git+https://github.com/greatbridf/intrusive-rs#0e2d88bffc9df606566fba2d61d1217182b06975"
 dependencies = [
- "autocfg",
- "scopeguard",
+ "memoffset",
 ]
 
 [[package]]
-name = "log"
-version = "0.4.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+name = "intrusive_list"
+version = "0.1.0"
 
 [[package]]
-name = "memchr"
-version = "2.7.4"
+name = "itertools"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
 
 [[package]]
-name = "minimal-lexical"
-version = "0.2.1"
+name = "log"
+version = "0.4.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 
 [[package]]
-name = "nom"
-version = "7.1.3"
+name = "loongArch64"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+checksum = "7c9f0d275c70310e2a9d2fc23250c5ac826a73fa828a5f256401f85c5c554283"
 dependencies = [
- "memchr",
- "minimal-lexical",
+ "bit_field",
+ "bitflags",
 ]
 
 [[package]]
-name = "percpu-macros"
-version = "0.1.0"
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "autocfg",
 ]
 
 [[package]]
-name = "prettyplease"
-version = "0.2.25"
+name = "paste"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pointers"
+version = "0.1.0"
+
+[[package]]
+name = "posix_types"
+version = "0.1.0"
 dependencies = [
- "proc-macro2",
- "syn",
+ "bitflags",
+ "cfg-if",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.92"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.37"
+version = "1.0.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
-name = "regex"
-version = "1.11.1"
+name = "riscv"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+checksum = "afa3cdbeccae4359f6839a00e8b77e5736caa200ba216caf38d24e4c16e2b586"
 dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
+ "critical-section",
+ "embedded-hal",
+ "paste",
+ "riscv-macros",
+ "riscv-pac",
 ]
 
 [[package]]
-name = "regex-automata"
-version = "0.4.9"
+name = "riscv-macros"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+checksum = "e8c4aa1ea1af6dcc83a61be12e8189f9b293c3ba5a487778a4cd89fb060fdbbc"
 dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
-name = "regex-syntax"
-version = "0.8.5"
+name = "riscv-pac"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436"
 
 [[package]]
-name = "rustc-hash"
-version = "1.1.0"
+name = "safe-mmio"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+checksum = "db02a82ad13df46afeba34a4e54065fa912308b9101b060e4422898eac0e06f6"
+dependencies = [
+ "zerocopy",
+]
 
 [[package]]
-name = "scopeguard"
-version = "1.2.0"
+name = "sbi"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+checksum = "5c64a569a412de4ad7b123f429e434751d74dd7ed25654af962b93c4d1cd584e"
 
 [[package]]
-name = "shlex"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+name = "slab_allocator"
+version = "0.1.0"
+dependencies = [
+ "eonix_mm",
+ "eonix_sync",
+ "intrusive_list",
+]
 
 [[package]]
-name = "spin"
-version = "0.9.8"
+name = "stalloc"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-dependencies = [
- "lock_api",
-]
+checksum = "a37f0ead4094eeb54c6893316aa139e48b252f1c07511e5124fa1f9414df5b6c"
 
 [[package]]
 name = "syn"
-version = "2.0.89"
+version = "2.0.104"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -278,71 +434,86 @@ dependencies = [
 ]
 
 [[package]]
-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"
+name = "thiserror"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
 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",
+ "thiserror-impl",
 ]
 
 [[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
+name = "thiserror-impl"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
 
 [[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
+name = "unicode-ident"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
 
 [[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
+name = "unwinding"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+checksum = "60612c845ef41699f39dc8c5391f252942c0a88b7d15da672eff0d14101bbd6d"
+dependencies = [
+ "gimli",
+]
 
 [[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
+name = "virtio-drivers"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+checksum = "7fe3f779fd88436e27b51540d9563c7454c8c814893a1e6f9bb6138bcac60627"
+dependencies = [
+ "bitflags",
+ "embedded-io",
+ "enumn",
+ "log",
+ "safe-mmio",
+ "thiserror",
+ "zerocopy",
+]
 
 [[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
+name = "xmas-elf"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+checksum = "18245fcbb8b3de8dd198ce7944fdd4096986fd6cd306b0fcfa27df817bd545d6"
+dependencies = [
+ "zero",
+]
 
 [[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
+name = "zero"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784"
 
 [[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
+name = "zerocopy"
+version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
+dependencies = [
+ "zerocopy-derive",
+]
 
 [[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
+name = "zerocopy-derive"
+version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]

+ 60 - 16
Cargo.toml

@@ -1,36 +1,80 @@
 [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" }
+atomic_unique_refcell = { path = "./crates/atomic_unique_refcell", features = [
+    "no_std",
+] }
+buddy_allocator = { path = "./crates/buddy_allocator" }
+eonix_hal = { path = "./crates/eonix_hal" }
+eonix_macros = { path = "./macros" }
+eonix_mm = { path = "./crates/eonix_mm" }
+eonix_percpu = { path = "./crates/eonix_percpu" }
+eonix_preempt = { path = "./crates/eonix_preempt" }
+eonix_runtime = { path = "./crates/eonix_runtime" }
+eonix_sync = { path = "./crates/eonix_sync" }
+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 = { version = "0.9.8", git = "https://github.com/greatbridf/intrusive-rs" }
 itertools = { version = "0.13.0", default-features = false }
-lazy_static = { version = "1.5.0", features = ["spin_no_std"] }
-spin = "0.9.8"
+acpi = "5.2.0"
+align_ext = "0.1.0"
+xmas-elf = "0.10.0"
+another_ext4 = { git = "https://github.com/SMS-Derfflinger/another_ext4", branch = "main" }
+stalloc = { version = "0.6.1", default-features = false, features = [
+    "allocator-api",
+] }
+
+[target.'cfg(any(target_arch = "riscv64", target_arch = "loongarch64"))'.dependencies]
+virtio-drivers = { version = "0.11.0" }
+
+[target.'cfg(target_arch = "riscv64")'.dependencies]
+unwinding = { version = "0.2.8", default-features = false, features = [
+    "unwinder",
+    "fde-static",
+    "personality",
+    "panic",
+] }
 
 [features]
-default = ["smp"]
-debug_syscall = []
+default = []
+trace_pci = []
+trace_syscall = []
+trace_scheduler = ["eonix_runtime/trace_scheduler"]
+log_trace = ["trace_pci", "trace_syscall", "trace_scheduler"]
+log_debug = []
 smp = []
 
-[build-dependencies]
-bindgen = "0.70.1"
+[profile.release]
+debug = true
 
-[profile.dev]
-panic = "abort"
+[profile.dev.package.eonix_preempt]
+opt-level = "s"
 
-[profile.dev.package.core]
-opt-level = 2
-debug = true
+[profile.dev.package.eonix_runtime]
+opt-level = "s"
+
+[profile.dev.package.eonix_sync]
+opt-level = "s"
+
+[profile.dev.package.intrusive_list]
+opt-level = "s"
+
+[profile.dev.package.eonix_hal]
+opt-level = "s"
 
 [profile.dev.package."*"]
-opt-level = 2
-debug = false
+opt-level = "s"
 
 [profile.dev.build-override]
 opt-level = 0

+ 180 - 35
Makefile.src

@@ -1,55 +1,162 @@
-# 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
-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)
-
-CROSS_COMPILE=##PLACEHOLDER_4##
-.PHONY: run
-run: build
-	$(QEMU_BIN) $(QEMU_ARGS) -display curses -S -s
-.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
+HOST ?= $(shell uname -s | tr '[:upper:]' '[:lower:]')
+ARCH ?= ##DEFAULT_ARCH##
+MODE ?= debug
+SMP ?= 4
+
+QEMU ?= ##QEMU##
+GDB ?= ##GDB##
+FDISK ?= ##FDISK##
+
+IMG ?= ##IMAGE##
+
+COMMA := ,
+
+PROFILE = $(MODE)
+ifeq ($(MODE),debug)
+PROFILE := dev
+endif
+
+USER_PROGRAMS = $(shell find user-programs -type f)
+KERNEL_SOURCES := $(shell find src macros crates -name '*.rs' -type f)
+KERNEL_CARGO_MANIFESTS += $(shell find src macros crates -name Cargo.toml -type f)
+KERNEL_DEPS := $(KERNEL_SOURCES) $(KERNEL_CARGO_MANIFESTS)
+
+QEMU_ARGS ?= -no-reboot
+CARGO_FLAGS := --profile $(PROFILE)
+
+ifneq ($(SMP),)
+CARGO_FLAGS += --features smp
+endif
+
+ifneq ($(FEATURES),)
+CARGO_FLAGS += --features $(FEATURES)
+endif
+
+ifeq ($(HOST),darwin)
+QEMU_ACCEL ?= -accel tcg
+else ifeq ($(HOST),linux)
+
+ifeq ($(shell ls /dev/kvm),/dev/kvm)
+QEMU_ACCEL ?= -accel kvm
+endif
+
+endif
+
+QEMU_ARGS += $(QEMU_ACCEL)
+
+ifneq ($(DEBUG_TRAPS),)
+QEMU_ARGS += -d cpu_reset,int,guest_errors -D build/qemu.log
+endif
+
+ifneq ($(SMP),)
+QEMU_ARGS += -smp $(SMP)
+endif
+
+ifeq ($(ARCH),riscv64)
+
+BINARY_DIR_BASE := build/riscv64gc-unknown-none-elf
+BINARY_DIR := $(BINARY_DIR_BASE)/$(MODE)
+
+QEMU_ARGS += \
+	-machine virt -kernel $(BINARY_DIR)/eonix_kernel \
+	-device virtio-blk-device,drive=disk0,bus=virtio-mmio-bus.0 \
+	-device virtio-net-device,netdev=mynet0 \
+	-drive id=disk0,file=build/boot-riscv64.img,format=raw,if=none \
+	-netdev user,id=mynet0 \
+	-rtc base=utc
+
+ifneq ($(IMG),)
+QEMU_ARGS += \
+	-drive id=disk1,file=$(IMG),format=raw,if=none \
+	-device virtio-blk-device,drive=disk1,bus=virtio-mmio-bus.1
+endif
+
+CARGO_FLAGS += --target riscv64gc-unknown-none-elf
+
+.PHONY: build
+build: $(BINARY_DIR)/eonix_kernel build/boot-riscv64.img
+
+else ifeq ($(ARCH),loongarch64)
 
-.PHONY: prepare
-prepare:
-	cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug $(CROSS_COMPILE)
-	cp build/compile_commands.json .
+BINARY_DIR_BASE := build/loongarch64-unknown-none-softfloat
+BINARY_DIR := $(BINARY_DIR_BASE)/$(MODE)
 
-.PHONY: reprepare
-reprepare: clean prepare
-	true
+QEMU_ARGS += \
+	-machine virt -kernel $(BINARY_DIR)/eonix_kernel -m 1G \
+	-device virtio-blk-pci,drive=disk0 \
+	-device virtio-net-pci,netdev=mynet0 \
+	-drive id=disk0,file=build/boot-loongarch64.img,format=raw,if=none \
+	-netdev user,id=mynet0,hostfwd=tcp::5555-:5555,hostfwd=udp::5555-:5555 \
+	-rtc base=utc
+
+ifneq ($(IMG),)
+QEMU_ARGS += \
+	-drive id=disk1,file=$(IMG),format=raw,if=none \
+	-device virtio-blk-pci,drive=disk1
+endif
+
+CARGO_FLAGS += --target loongarch64-unknown-none-softfloat
+
+.PHONY: build
+build: $(BINARY_DIR)/eonix_kernel build/boot-loongarch64.img
+
+else ifeq ($(ARCH),x86_64)
+
+BINARY_DIR_BASE := build/x86_64-unknown-none
+BINARY_DIR := $(BINARY_DIR_BASE)/$(MODE)
+
+QEMU_ARGS += \
+	-machine q35 \
+	-device ahci,id=ahci \
+	-device ide-hd,drive=disk0,bus=ahci.0 \
+	-device e1000e,netdev=mynet0 \
+	-drive id=disk0,file=build/boot-x86_64.img,format=raw,if=none \
+	-netdev user,id=mynet0
+
+ifneq ($(IMG),)
+QEMU_ARGS += \
+	-drive id=disk1,file=$(IMG),format=raw,if=none \
+	-device ide-hd,drive=disk1,bus=ahci.1
+endif
+
+CARGO_FLAGS += --target x86_64-unknown-none.json
 
 .PHONY: build
-build:
-	cmake --build build -j 6 --target boot.img
+build: $(BINARY_DIR)/eonix_kernel build/boot-x86_64.img
+
+endif
+
+.PHONY: run
+run: build build/kernel.sym
+	$(QEMU) $(QEMU_ARGS) -display none -serial mon:stdio
+
+.PHONY: srun
+srun: build build/kernel.sym
+	$(QEMU) $(QEMU_ARGS) -display none -S -s -serial mon:stdio
+
+.PHONY: test-run
+test-run: build
+	$(QEMU) $(QEMU_ARGS) -display none -serial stdio
 
 .PHONY: clean
 clean:
 	-rm -rf build
-	-rm compile_commands.json
+	-mkdir build
 
 .PHONY: clean-all
 clean-all: clean
 	-rm Makefile
 
 .PHONY: debug
-debug:
-	-$(GDB_BIN) --symbols=build/kernel.out \
+debug: build/kernel.sym
+	-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:
@@ -60,6 +167,44 @@ tmux-debug:
 	-tmux attach -t gbos-debug
 	tmux kill-session -t gbos-debug
 
-build/boot.vdi: build/boot.img
-	-rm build/boot.vdi
-	VBoxManage convertfromraw $< $@ --format VDI
+$(BINARY_DIR)/eonix_kernel: $(KERNEL_DEPS)
+	CARGO_TARGET_DIR=build cargo build $(CARGO_FLAGS)
+
+build/kernel.sym: $(BINARY_DIR)/eonix_kernel
+	CARGO_TARGET_DIR=build cargo objcopy -q $(CARGO_FLAGS) -- --only-keep-debug build/kernel.sym
+
+build/fs-%.img: user-programs/init_script_%.sh script/build-img.sh $(USER_PROGRAMS)
+	ARCH=$* OUTPUT=$@ sh script/build-img.sh
+
+build/mbr.bin: $(BINARY_DIR)/eonix_kernel
+	CARGO_TARGET_DIR=build cargo objcopy -q $(CARGO_FLAGS) -- -O binary -j .mbr build/mbr.bin
+
+build/stage1.bin: $(BINARY_DIR)/eonix_kernel
+	CARGO_TARGET_DIR=build cargo objcopy -q $(CARGO_FLAGS) -- -O binary -j .stage1 build/stage1.bin
+
+build/kernel.bin: $(BINARY_DIR)/eonix_kernel
+	CARGO_TARGET_DIR=build cargo objcopy -q $(CARGO_FLAGS) -- -O binary --strip-debug \
+		-R .mbr -R .stage1 build/kernel.bin
+
+build/boot-x86_64.img: build/fs-x86_64.img build/mbr.bin build/stage1.bin build/kernel.bin
+	dd if=build/mbr.bin of=$@ bs=512 count=1 conv=notrunc 2> /dev/null
+	dd if=build/stage1.bin of=$@ bs=512 seek=1 conv=notrunc 2> /dev/null
+	dd if=build/kernel.bin of=$@ bs=4096 seek=1 conv=notrunc 2> /dev/null
+	dd if=$< of=$@ bs=$(shell expr 4 \* 1024 \* 1024) \
+		seek=1 conv=notrunc 2> /dev/null
+	sh -c 'echo n; echo; echo; echo 8192; echo; echo a; echo w' \
+		| $(FDISK) $@ 2> /dev/null > /dev/null
+
+build/boot-riscv64.img: build/fs-riscv64.img
+	dd if=$< of=$@ bs=$(shell expr 4 \* 1024 \* 1024) \
+		seek=1 conv=notrunc 2> /dev/null
+	sh -c 'echo n; echo; echo; echo 8192; echo; echo a; echo w' \
+		| $(FDISK) $@ 2> /dev/null > /dev/null
+
+build/boot-loongarch64.img: build/fs-loongarch64.img
+	dd if=$< of=$@ bs=$(shell expr 4 \* 1024 \* 1024) \
+		seek=1 conv=notrunc 2> /dev/null
+	sh -c 'echo n; echo; echo; echo 8192; echo; echo a; echo w' \
+		| $(FDISK) $@ 2> /dev/null > /dev/null
+
+.DEFAULT_GOAL := build

+ 193 - 41
README.md

@@ -1,69 +1,221 @@
-# gbos
+# Eonix
 
-A simple OS kernel written in C++ and Rust that aims to be Linux compatible out of the box.
+*Eonix* 是一个基于 *Rust* 编写的开源项目,旨在提供一个适用于多处理器多架构的宏内核操作系统。
 
-- [x] Multitasking
-- [x] Memory management
-- [x] Filesystem implementation
-- [x] ELF program loader
-- [x] Some rather naive AHCI driver and FAT32 impl.
-- [x] TTY and job control interface (partially working)
-- [ ] Move to Rust (WIP)
-- [ ] SMP support (WIP)
-- [ ] POSIX thread support (WIP)
-- [ ] Network stack (WIP)
-- [ ] Dynamic loader support
-- [ ] Users, permission...
+*Eonix* 项目的目标是创建一个高效、安全、可靠的操作系统。我们的目标是提供一个简单的内核设计,使得它易于理解和维护。同时,我们也希望 *Eonix* 能够提供足够复杂的特性,如虚拟内存管理、多进程支持、*POSIX* 兼容的文件系统模型、易于使用的设备驱动开发接口等,使其拥有较强的实用性。
 
-# Build & Run
+## 项目特性
 
-## Prerequisites
+- [x] 多处理器支持
+- [x] 多架构支持(平台相关接口抽象,目前只有 x86_64 一种实现,正在加入其他架构的实现)
+- [x] 内存管理
+- [x] 进程管理
+- [x] 文件系统
+- [x] *POSIX* 兼容性,兼容 *Linux* 系统调用接口
+- [x] 静态 ELF 加载器
+- [x] TTY 及任务控制接口(正在进一步实现)
+- [x] FAT32 文件系统的读取实现
+- [x] 全部 Rust 化(只剩一点点)
+- [ ] 网卡驱动(WIP)
+- [ ] POSIX 线程接口(WIP)
+- [ ] 动态加载器(WIP)
+- [ ] 用户、权限(WIP)
 
-#### Compile
+项目经测试可使用 *busybox* 中大多数功能,并且该内核使用 *busybox* 搭配 init 脚本启动,运行 *busybox* 提供的 *ash* 来执行 shell 命令。
 
-- [GCC (Tested)](https://gcc.gnu.org/) or
-- [Clang (It should work, but haven't been tested for a while.)](https://clang.llvm.org/)
+![测试](doc/images/image-0.png)
+![test pipes](doc/images/image-1.png)
 
-- [Rust](https://www.rust-lang.org/)
+## 初赛文档
+
+本项目开发时代码仓库在 [GitHub](https://github.com/greatbridf/osdev.git)。
+
+- [启动加载](https://gitlab.eduxiji.net/T202410247994622/project2608132-275906/-/blob/master/doc/boot.md)
+- [内存管理](https://gitlab.eduxiji.net/T202410247994622/project2608132-275906/-/blob/master/doc/memory.md)
+- [进程管理](https://gitlab.eduxiji.net/T202410247994622/project2608132-275906/-/blob/master/doc/task.md)
+- [文件系统](https://gitlab.eduxiji.net/T202410247994622/project2608132-275906/-/blob/master/doc/filesystem.md)
+- [设备驱动](https://gitlab.eduxiji.net/T202410247994622/project2608132-275906/-/blob/master/doc/device_driver.md)
+- [多平台支持](https://gitlab.eduxiji.net/T202410247994622/project2608132-275906/-/blob/master/doc/multi_arch.md)
+
+## 代码树结构
+
+```
+.
+├── arch                          # 平台相关代码
+│   ├── percpu-macros             # percpu变量宏
+│   └── src
+│       └── x86_64                # x86_64
+│           ├── context.rs        # 上下文切换相关
+│           ├── gdt.rs            # x86 架构 GDT相关
+│           ├── init.rs           # 内核初始化相关
+│           ├── interrupt.rs      # 中断相关
+│           ├── io.rs             # IO 相关
+│           ├── percpu.rs         # percpu 变量相关
+│           └── user.rs           # 用户态相关
+├── busybox                       # 默认选项编译的busybox
+├── busybox-minimal               # 预编译的精简版busybox
+├── configure                     # 编译准备用
+├── doc                           # 开发文档
+├── gblibc                        # C 标准库实现
+├── gblibstdc++                   # C++ 标准库
+├── include                       # C++ 头文件
+├── init_script.sh                # init 进程使用的脚本文件
+├── src
+│   ├── boot.s                    # 启动代码
+│   ├── driver
+│   │   ├── ahci                  # AHCI 控制器驱动
+│   │   ├── e1000e                # e1000e 网卡驱动(WIP)
+│   │   └── serial.rs             # 串口驱动
+│   ├── driver.rs                 # 驱动相关
+│   ├── elf.rs                    # ELF 加载器
+│   ├── fs
+│   │   ├── fat32.rs              # FAT32 实现
+│   │   ├── procfs.rs             # procfs 实现
+│   │   └── tmpfs.rs              # ramfs 实现
+│   ├── hash.rs                   # Hash 函数等
+│   ├── intrusive_list.rs         # 侵入式链表
+│   ├── io.rs                     # IO 工具类
+│   ├── kernel
+│   │   ├── allocator.cc          # C++ 分配器
+│   │   ├── async
+│   │   │   └── lock.cc           # C++ 中自旋锁
+│   │   ├── block.rs              # 块设备抽象
+│   │   ├── chardev.rs            # 字符设备抽象
+│   │   ├── console.rs            # 内核 console 抽象
+│   │   ├── constants.rs          # 常量定义
+│   │   ├── cpu.rs                # CPU 状态相关
+│   │   ├── hw
+│   │   │   ├── acpi.cc           # ACPI 数据相关(待移除)
+│   │   │   └── pci.cc            # PCI 设备相关(待移除)
+│   │   ├── interrupt.rs          # 中断相关(共通)
+│   │   ├── mem
+│   │   │   ├── address.rs        # 虚拟地址等抽象
+│   │   │   ├── mm_area.rs        # 映射区域
+│   │   │   ├── mm_list
+│   │   │   │   └── page_fault.rs # 处理Page Fault
+│   │   │   ├── mm_list.rs        # 地址空间
+│   │   │   ├── page_alloc.rs     # 页分配器
+│   │   │   ├── page_table.rs     # 页表
+│   │   │   ├── paging.rs         # 分页相关
+│   │   │   ├── phys.rs           # 物理地址访问相关
+│   │   │   └── slab.cc           # Slab 分配器
+│   │   ├── smp.rs                # 多处理器相关
+│   │   ├── syscall
+│   │   │   ├── file_rw.rs        # 文件相关syscall
+│   │   │   ├── mm.rs             # 内存相关syscall
+│   │   │   ├── net.rs            # 网络相关syscall
+│   │   │   ├── procops.rs        # 进程管理相关syscall
+│   │   │   └── sysinfo.rs        # sys_utsname
+│   │   ├── syscall.rs            # syscall相关定义
+│   │   ├── task
+│   │   │   ├── kstack.rs         # 内核栈操作
+│   │   │   ├── process.rs        # 进程
+│   │   │   ├── process_group.rs  # 进程组
+│   │   │   ├── process_list.rs   # 进程列表
+│   │   │   ├── scheduler.rs      # 调度器
+│   │   │   ├── session.rs        # 会话
+│   │   │   ├── signal.rs         # 信号处理
+│   │   │   └── thread.rs         # 线程
+│   │   ├── task.rs               # 任务模块
+│   │   ├── terminal.rs           # 终端抽象
+│   │   ├── timer.rs              # 时钟模块
+│   │   ├── user
+│   │   │   └── dataflow.rs       # 用户空间数据访问
+│   │   ├── user.rs
+│   │   └── vfs
+│   │       ├── dentry
+│   │       │   └── dcache.rs     # Dentry 缓存
+│   │       ├── dentry.rs         # Dentry
+│   │       ├── file.rs           # 打开的文件
+│   │       ├── filearray.rs      # 用户的文件数组
+│   │       ├── inode.rs          # Inode
+│   │       ├── mount.rs          # 文件系统挂载
+│   │       └── vfs.rs            # 虚拟文件系统
+│   ├── kernel.ld                 # 链接脚本
+│   ├── kernel.rs
+│   ├── kinit.cpp            # 初始化
+│   ├── lib.rs
+│   ├── mbr.S                # MBR 代码
+│   ├── net
+│   │   └── netdev.rs        # 网络设备抽象
+│   ├── path.rs              # 路径处理
+│   ├── rcu.rs               # RCU 的一个临时实现
+│   ├── sync
+│   │   ├── condvar.rs       # 条件变量
+│   │   ├── lock.rs          # 抽象锁类
+│   │   ├── locked.rs        # 依赖锁变量
+│   │   ├── semaphore.rs     # 信号量
+│   │   ├── spin.rs          # 自旋锁
+│   │   └── strategy.rs      # 锁策略
+│   ├── sync.rs              # 同步模块
+│   └── types
+│       └── libstdcpp.cpp    # C++ 标准库用
+├── user-space-program       # 测试用用户程序
+└── x86_64-unknown-none.json # Rust 用 x86_64 工具链文件
+```
+
+# 编译 & 运行
+
+## 构建依赖
+
+#### 编译
+
+- [GCC (测试过)](https://gcc.gnu.org/) or
+
+- [Rust(nightly-2024-12-18)](https://www.rust-lang.org/)
 - [CMake](https://cmake.org/)
 
-#### Generate disk image
+#### 生成硬盘镜像
 
-- [(Optional) busybox (We have a prebuilt version of busybox in the project directory)](https://www.busybox.net/)
+- [(Optional) busybox (项目目录下有预编译过的busybox)](https://www.busybox.net/)
 - [fdisk](https://www.gnu.org/software/fdisk/)
 - [mtools](http://www.gnu.org/software/mtools/)
 
-#### Debug and Run
+#### 调试 & 运行
 
 - [GDB](https://www.gnu.org/software/gdb/)
 - [QEMU](https://www.qemu.org/)
 
-## Build and Run
+## 编译及运行命令
 
 ```bash
-./configure && make prepare && make build
+# 配置构建环境
+./configure && make build
 
-make nativerun
+# 直接运行
 
-# or if you need debugging
+make run
 
-# 1:
+# 如果需要调试
+
+# 1: 启动调试
 make srun
 
-# 2:
+# 2: 启动调试器
 make debug
-```
 
-You may want to specify the correct version of build tools to use when running `./configure` in ENV.
+# 或者如果有 tmux
 
-- `QEMU`: QEMU used to debug run. `qemu-system-x86_64` by default.
-- `GDB`: GDB used by `make debug`. We will search for `gdb` and `x86_64-elf-gdb` and check the supported archs by default.
-- `FDISK_BIN`: fdisk executable used to create disk image part tables. `fdisk` by default.
-
-If you are doing a cross compile, run `./configure` with `CROSS_COMPILE` set to the corresponding target triple of your cross compiler.
-
-## Run your own program
-
-The `user` directory under the project dir exists mainly for some *historical* reason and has almost no use. So don't ever try to look inside that.
+make tmux-debug
+```
 
-To copy your program into the built disk image, you can edit the `CMakeLists.txt` and add a line to the `boot.img` section. You can also try editing the `init_script.sh` to customize the booting procedure.
+可能需要在运行 `./configure` 时在环境变量中指定正确版本的构建工具。
+
+- `DEFAULT_ARCH`: 在调用 Makefile 时如果不进行额外指定,默认使用的架构。默认为 `x86_64`。
+- `QEMU`: 用于调试运行的 QEMU。默认使用 `qemu-system-$(ARCH)`。
+- `GDB`: 用于 `make debug` 的 GDB。我们将默认查找 `$(ARCH)-elf-gdb` 并检查支持的架构。
+- `FDISK`: 用于创建磁盘镜像分区表的 fdisk 可执行文件,要求使用来自 util-linux 版本的 fdisk。默认使用 `fdisk`。
+- `IMG`: 除启动磁盘以外,额外的磁盘镜像文件。默认不使用。
+
+在运行 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 日志。
+- `FEATURES`: 手动指定要编译的特性,使用逗号分隔。具体见 `Cargo.toml` 中的 `features` 字段。

+ 0 - 68
arch/Cargo.lock

@@ -1,68 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "arch"
-version = "0.1.0"
-dependencies = [
- "cfg-if",
- "percpu",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "percpu"
-version = "0.1.0"
-dependencies = [
- "percpu-macros",
-]
-
-[[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 - 8
arch/Cargo.toml

@@ -1,8 +0,0 @@
-[package]
-name = "arch"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-percpu-macros = { path="./percpu-macros" }
-cfg-if = "1.0"

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

@@ -1,54 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "percpu-macros"
-version = "0.1.0"
-dependencies = [
- "cfg-if",
- "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 - 21
arch/percpu-macros/src/arch.rs

@@ -1,21 +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 - 117
arch/percpu-macros/src/lib.rs

@@ -1,117 +0,0 @@
-extern crate proc_macro;
-
-use proc_macro::TokenStream;
-use quote::{format_ident, quote};
-use syn::{parse_macro_input, ItemStatic};
-
-mod arch;
-
-#[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;
-
-    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());
-
-    let is_atomic_like = is_bool || is_integer || quote!(#ty).to_string().contains("NonNull");
-
-    let inner_ident = format_ident!("_percpu_inner_{}", ident);
-    let access_ident = format_ident!("_access_{}", ident);
-
-    let integer_methods = if is_integer {
-        quote! {
-            pub fn add(&self, value: #ty) {
-                *unsafe { self.as_mut() } += value;
-            }
-
-            pub fn sub(&self, value: #ty) {
-                *unsafe { self.as_mut() } -= value;
-            }
-        }
-    } else {
-        quote! {}
-    };
-
-    let preempt_disable = if !is_atomic_like {
-        quote! { crate::sync::preempt::disable(); }
-    } else {
-        quote! {}
-    };
-
-    let preempt_enable = if !is_atomic_like {
-        quote! { crate::sync::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
-        }
-    }
-    .into()
-}

+ 0 - 14
arch/src/lib.rs

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

+ 0 - 71
arch/src/x86_64/context.rs

@@ -1,71 +0,0 @@
-use core::arch::naked_asm;
-
-#[repr(C)]
-#[derive(Debug, Default)]
-struct ContextSwitchFrame {
-    r15: u64,
-    r14: u64,
-    r13: u64,
-    r12: u64,
-    rbx: u64,
-    rbp: u64,
-    eflags: u64,
-    rip: u64,
-}
-
-/// Necessary hardware states of task for context switch
-pub struct TaskContext {
-    /// The kernel stack pointer
-    pub rsp: u64,
-    // Extended states, i.e., FP/SIMD states to do!
-}
-
-impl TaskContext {
-    pub const fn new() -> Self {
-        Self { rsp: 0 }
-    }
-
-    pub fn init(&mut self, entry: usize, kstack_top: usize) {
-        unsafe {
-            let frame_ptr = (kstack_top as *mut ContextSwitchFrame).sub(1);
-            core::ptr::write(
-                frame_ptr,
-                ContextSwitchFrame {
-                    rip: entry as u64,
-                    eflags: 0x200,
-                    ..Default::default()
-                },
-            );
-            self.rsp = frame_ptr as u64;
-        }
-    }
-
-    #[inline(always)]
-    pub fn switch_to(&mut self, next_task: &mut Self) {
-        unsafe { _switch_to(&mut self.rsp, &mut next_task.rsp) }
-    }
-}
-
-#[naked]
-unsafe extern "C" fn _switch_to(current_context_sp: &mut u64, next_context_sp: &mut u64) {
-    naked_asm!(
-        "pushf",
-        "push %rbp",
-        "push %rbx",
-        "push %r12",
-        "push %r13",
-        "push %r14",
-        "push %r15",
-        "mov %rsp, (%rdi)",
-        "mov (%rsi), %rsp",
-        "pop %r15",
-        "pop %r14",
-        "pop %r13",
-        "pop %r12",
-        "pop %rbx",
-        "pop %rbp",
-        "popf",
-        "ret",
-        options(att_syntax),
-    );
-}

+ 0 - 241
arch/src/x86_64/init.rs

@@ -1,241 +0,0 @@
-use core::{
-    alloc::Layout,
-    pin::Pin,
-    ptr::{addr_of, NonNull},
-};
-
-use super::{enable_sse, percpu::init_percpu_area_thiscpu, GDTEntry, InterruptControl, GDT};
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-#[allow(non_camel_case_types)]
-struct TSS_SP {
-    low: u32,
-    high: u32,
-}
-
-#[repr(C)]
-pub struct TSS {
-    _reserved1: u32,
-    rsp: [TSS_SP; 3],
-    _reserved2: u32,
-    _reserved3: u32,
-    ist: [TSS_SP; 7],
-    _reserved4: u32,
-    _reserved5: u32,
-    _reserved6: u16,
-    iomap_base: u16,
-}
-
-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;
-    }
-}
-
-/// Architecture-specific per-cpu status.
-#[allow(dead_code)]
-pub struct CPUStatus {
-    id: usize,
-    gdt: GDT,
-    tss: TSS,
-
-    percpu_area: NonNull<u8>,
-    pub interrupt: InterruptControl,
-}
-
-impl CPUStatus {
-    pub unsafe fn new_thiscpu<F>(allocate: F) -> Self
-    where
-        F: FnOnce(Layout) -> NonNull<u8>,
-    {
-        const PAGE_SIZE: usize = 0x1000;
-        extern "C" {
-            static PERCPU_PAGES: usize;
-            fn _PERCPU_DATA_START();
-        }
-
-        let percpu_area = allocate(Layout::from_size_align_unchecked(
-            PERCPU_PAGES * PAGE_SIZE,
-            PAGE_SIZE,
-        ));
-
-        percpu_area.copy_from_nonoverlapping(
-            NonNull::new(_PERCPU_DATA_START as *mut u8).unwrap(),
-            PERCPU_PAGES * PAGE_SIZE,
-        );
-
-        let (interrupt_control, cpuid) = InterruptControl::new();
-
-        init_percpu_area_thiscpu(percpu_area);
-        Self {
-            id: cpuid,
-            gdt: GDT::new(),
-            tss: TSS::new(),
-            percpu_area,
-            interrupt: interrupt_control,
-        }
-    }
-
-    /// Load GDT and TSS in place.
-    ///
-    /// # 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();
-
-        let tss_addr = addr_of!(self_mut.tss);
-        self_mut.gdt.set_tss(tss_addr as u64);
-        self_mut.gdt.load();
-
-        // SAFETY: `self` is pinned, so are its fields.
-        Pin::new_unchecked(&mut self_mut.interrupt).setup_idt();
-        self_mut.interrupt.setup_timer();
-    }
-
-    /// Bootstrap all CPUs.
-    /// This should only be called on the BSP.
-    pub unsafe fn bootstrap_cpus(&self) {
-        self.interrupt.send_sipi();
-    }
-
-    pub unsafe fn set_rsp0(&mut self, rsp: u64) {
-        self.tss.set_rsp0(rsp);
-    }
-
-    pub unsafe fn set_tls32(&mut self, desc: GDTEntry) {
-        self.gdt.set_tls32(desc);
-    }
-
-    pub fn cpuid(&self) -> usize {
-        self.id
-    }
-}
-
-#[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::arch::global_asm!(
-            r#"
-        .pushsection .stage1.smp
-        .code16
-        .globl ap_bootstrap
-        .type ap_bootstrap, @function
-        ap_bootstrap:
-            ljmp $0x0, $.Lap1
-
-        .Lap1:
-            # we use the shared gdt for cpu bootstrapping
-            lgdt .Lshared_gdt_desc
-
-            # 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, $.Lap_bootstrap_end
-
-        .align 16
-        .Lshared_gdt_desc:
-            .8byte 0x0000000000005f
-
-        .code64
-        .Lap_bootstrap_end:
-            mov $0x10, %ax
-            mov %ax, %ds
-            mov %ax, %es
-            mov %ax, %ss
-
-            xor %rsp, %rsp
-            xor %rax, %rax
-            inc %rax
-        1:
-            xchg %rax, {BOOT_SEMAPHORE}
-            cmp $0, %rax
-            je 1f
-            pause
-            jmp 1b
-
-        1:
-            mov {BOOT_STACK}, %rsp # Acquire
-            cmp $0, %rsp
-            jne 1f
-            pause
-            jmp 1b
-
-        1:
-            xor %rax, %rax
-            mov %rax, {BOOT_STACK} # Release
-            xchg %rax, {BOOT_SEMAPHORE}
-
-            lock incq {CPU_COUNT}
-
-            xor %rbp, %rbp
-            push %rbp # NULL return address
-            jmp {AP_ENTRY}
-            .popsection
-            "#,
-            KERNEL_PML4 = const 0x2000,
-            BOOT_SEMAPHORE = sym BOOT_SEMAPHORE,
-            BOOT_STACK = sym BOOT_STACK,
-            CPU_COUNT = sym CPU_COUNT,
-            AP_ENTRY = sym $ap_entry,
-            options(att_syntax),
-        );
-
-        pub unsafe fn wait_cpus_online() {
-            use core::sync::atomic::Ordering;
-            while CPU_COUNT.load(Ordering::Acquire) != $cpu_count - 1 {
-                if BOOT_STACK.load(Ordering::Acquire) == 0 {
-                    let stack_bottom = $alloc_kstack as u64;
-                    BOOT_STACK.store(stack_bottom, Ordering::Release);
-                }
-                $crate::pause();
-            }
-        }
-    };
-}

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

@@ -1,480 +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
-    
-    	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,
-}
-
-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 unsafe 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()
-    }
-}
-
-pub fn enable_irqs() {
-    unsafe {
-        asm!("sti");
-    }
-}
-
-pub fn disable_irqs() {
-    unsafe {
-        asm!("cli");
-    }
-}
-
-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 - 0
arch/src/x86_64/interrupt.s


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

@@ -1,93 +0,0 @@
-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 {
-        asm!(
-            "inb %dx, %al",
-            in("dx") no,
-            out("al") data,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-
-    data
-}
-
-pub fn inw(no: u16) -> u16 {
-    let data;
-    unsafe {
-        asm!(
-            "inw %dx, %ax",
-            in("dx") no,
-            out("ax") data,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-
-    data
-}
-
-pub fn inl(no: u16) -> u32 {
-    let data;
-    unsafe {
-        asm!(
-            "inl %dx, %eax",
-            in("dx") no,
-            out("eax") data,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-
-    data
-}
-
-pub fn outb(no: u16, data: u8) {
-    unsafe {
-        asm!(
-            "outb %al, %dx",
-            in("al") data,
-            in("dx") no,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-}
-
-pub fn outw(no: u16, data: u16) {
-    unsafe {
-        asm!(
-            "outw %ax, %dx",
-            in("ax") data,
-            in("dx") no,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-}
-
-pub fn outl(no: u16, data: u32) {
-    unsafe {
-        asm!(
-            "outl %eax, %dx",
-            in("eax") data,
-            in("dx") no,
-            options(att_syntax, nomem, nostack)
-        )
-    };
-}

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

@@ -1,134 +0,0 @@
-mod context;
-mod gdt;
-mod init;
-mod interrupt;
-mod io;
-mod user;
-
-pub(self) mod percpu;
-
-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 percpu_macros::define_percpu;
-
-use core::arch::asm;
-
-#[inline(always)]
-pub fn flush_tlb(vaddr: usize) {
-    unsafe {
-        asm!(
-            "invlpg ({})",
-            in(reg) vaddr,
-            options(att_syntax)
-        );
-    }
-}
-
-#[inline(always)]
-pub fn flush_tlb_all() {
-    unsafe {
-        asm!(
-            "mov %cr3, %rax",
-            "mov %rax, %cr3",
-            out("rax") _,
-            options(att_syntax)
-        );
-    }
-}
-
-#[inline(always)]
-pub fn get_root_page_table() -> usize {
-    let cr3: usize;
-    unsafe {
-        asm!(
-            "mov %cr3, {0}",
-            out(reg) cr3,
-            options(att_syntax)
-        );
-    }
-    cr3
-}
-
-#[inline(always)]
-pub fn set_root_page_table(pfn: usize) {
-    unsafe {
-        asm!(
-            "mov {0}, %cr3",
-            in(reg) pfn,
-            options(att_syntax)
-        );
-    }
-}
-
-#[inline(always)]
-pub fn get_page_fault_address() -> usize {
-    let cr2: usize;
-    unsafe {
-        asm!(
-            "mov %cr2, {}",
-            out(reg) cr2,
-            options(att_syntax)
-        );
-    }
-    cr2
-}
-
-#[inline(always)]
-pub fn halt() {
-    unsafe {
-        asm!("hlt", options(att_syntax, nostack));
-    }
-}
-
-#[inline(always)]
-pub fn pause() {
-    unsafe {
-        asm!("pause", options(att_syntax, nostack));
-    }
-}
-
-#[inline(always)]
-pub fn freeze() -> ! {
-    loop {
-        interrupt::disable_irqs();
-        halt();
-    }
-}
-
-#[inline(always)]
-pub fn rdmsr(msr: u32) -> u64 {
-    let edx: u32;
-    let eax: u32;
-
-    unsafe {
-        asm!(
-            "rdmsr",
-            in("ecx") msr,
-            out("eax") eax,
-            out("edx") edx,
-            options(att_syntax),
-        );
-    }
-
-    (edx as u64) << 32 | eax as u64
-}
-
-#[inline(always)]
-pub fn wrmsr(msr: u32, value: u64) {
-    let eax = value as u32;
-    let edx = (value >> 32) as u32;
-
-    unsafe {
-        asm!(
-            "wrmsr",
-            in("ecx") msr,
-            in("eax") eax,
-            in("edx") edx,
-            options(att_syntax),
-        );
-    }
-}

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

@@ -1,16 +0,0 @@
-use super::wrmsr;
-use core::{arch::asm, ptr::NonNull};
-
-fn save_percpu_pointer(percpu_area_base: NonNull<u8>) {
-    wrmsr(0xC0000101, percpu_area_base.as_ptr() as u64);
-}
-
-pub unsafe fn init_percpu_area_thiscpu(percpu_area_base: NonNull<u8>) {
-    save_percpu_pointer(percpu_area_base);
-
-    asm!(
-        "movq {}, %gs:0",
-        in(reg) percpu_area_base.as_ptr(),
-        options(att_syntax)
-    );
-}

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

@@ -1,54 +0,0 @@
-use core::pin::Pin;
-
-use super::{CPUStatus, 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 CPUStatus>) {
-        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 CPUStatus>, stack: u64) {
-    // SAFETY: We don't move the CPUStatus object.
-    cpu_status.get_unchecked_mut().set_rsp0(stack);
-}

+ 0 - 13
bochs.conf

@@ -1,13 +0,0 @@
-megs: 64
-
-# floppya: 1_44=build/boot.img, status=inserted
-ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
-ata0-master: type=disk, path="build/boot.img", mode=flat, cylinders=0, heads=0, spt=0, model="Generic MyDisk", biosdetect=auto, translation=auto
-
-boot: disk
-
-mouse: enabled=0
-
-display_library: x, options="gui_debug"
-
-magic_break: enabled=1

+ 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(())
 }

+ 75 - 92
configure

@@ -1,41 +1,60 @@
 #!/bin/sh
-QEMU_EXECUTABLES="qemu-system-x86_64"
-GDB_EXECUTABLES="gdb x86_64-elf-gdb"
+DEFAULT_ARCH="x86_64"
+
+if [ "$OUT" = "" ]; then
+    OUT="Makefile"
+fi
+
+printf "Configuring for %s...\n" "$OUT"
 
 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")
+ARCH=${ARCH:-"$DEFAULT_ARCH"}
 
-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
-}
+# Define toolchain and QEMU/GDB settings for per architecture
+event "target architecture"
+echo "$ARCH"
+case "$ARCH" in
+    x86_64)
+        QEMU_EXECUTABLES="qemu-system-x86_64"
+        GDB_EXECUTABLES="gdb x86_64-elf-gdb"
+        : "${CROSS_COMPILE:=}"
+        ;;
+    riscv64)
+        QEMU_EXECUTABLES="qemu-system-riscv64"
+        GDB_EXECUTABLES="gdb riscv64-unknown-elf-gdb"
+        : "${CROSS_COMPILE:=riscv64-unknown-elf-}"
+        ;;
+    aarch64)
+        QEMU_EXECUTABLES="qemu-system-aarch64"
+        GDB_EXECUTABLES="gdb aarch64-none-elf-gdb"
+        : "${CROSS_COMPILE:=aarch64-none-elf-}"
+        ;;
+    *)
+        echo "Unsupported ARCH: $ARCH"
+        exit 1
+        ;;
+esac
 
-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-$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 +65,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 "$ARCH" >/dev/null 2>&1; then
         return 0
     else
         return 1
@@ -54,77 +73,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 "$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"
@@ -135,17 +118,17 @@ else
     echo `which mkfs.fat`
 fi
 
-event "checking mtools"
-if ! which mcopy > /dev/null 2>&1; then
+event "checking additional image"
+if [ "$IMG" = "" ]; then
     echo "no"
-    exit 1
 else
-    echo `which mcopy`
+    echo "$IMG"
 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
+cp Makefile.src "$OUT"
+sed -i '' -e "s|##DEFAULT_ARCH##|$ARCH|" "$OUT" > /dev/null 2>&1
+sed -i '' -e "s|##GDB##|$GDB|" "$OUT" > /dev/null 2>&1
+sed -i '' -e "s|##QEMU##|$QEMU|" "$OUT" > /dev/null 2>&1
+sed -i '' -e "s|##FDISK##|$FDISK|" "$OUT" > /dev/null 2>&1
+sed -i '' -e "s|##IMAGE##|$IMG|" "$OUT" > /dev/null 2>&1
 exit 0

+ 7 - 0
crates/atomic_unique_refcell/Cargo.lock

@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "atomic_unique_refcell"
+version = "0.1.0"

+ 10 - 0
crates/atomic_unique_refcell/Cargo.toml

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

+ 104 - 0
crates/atomic_unique_refcell/src/lib.rs

@@ -0,0 +1,104 @@
+#![cfg_attr(feature = "no_std", no_std)]
+
+#[cfg(feature = "no_std")]
+use core::{
+    cell::UnsafeCell,
+    marker::PhantomData,
+    ops::{Deref, DerefMut},
+    sync::atomic::{AtomicBool, Ordering},
+};
+
+#[cfg(not(feature = "no_std"))]
+use std::{
+    cell::UnsafeCell,
+    marker::PhantomData,
+    ops::{Deref, DerefMut},
+    sync::atomic::{AtomicBool, Ordering},
+};
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test() {
+        let cell = AtomicUniqueRefCell::new(42);
+        let mut ref_cell = cell.borrow();
+        assert_eq!(*ref_cell, 42);
+        *ref_cell = 43;
+        assert_eq!(*ref_cell, 43);
+    }
+}
+
+/// `AtomicUniqueRefCell` implements `Send` and `Sync` if `T` is `Send`.
+/// The following code will not compile if `T` is not `Send`.
+///
+/// ```compile_fail
+/// use atomic_unique_refcell::AtomicUniqueRefCell;
+///
+/// struct NotSend {
+///     data: *mut (),
+/// }
+///
+/// struct Test {
+///     data: AtomicUniqueRefCell<NotSend>,
+/// }
+///
+/// trait TestTrait: Send + Sync {}
+///
+/// impl TestTrait for Test {}
+/// ```
+pub struct AtomicUniqueRefCell<T: ?Sized> {
+    count: AtomicBool,
+    inner: UnsafeCell<T>,
+}
+
+unsafe impl<T: ?Sized + Send> Send for AtomicUniqueRefCell<T> {}
+unsafe impl<T: ?Sized + Send> Sync for AtomicUniqueRefCell<T> {}
+
+pub struct Ref<'a, T: ?Sized> {
+    inner: &'a AtomicUniqueRefCell<T>,
+    _marker: PhantomData<UnsafeCell<T>>,
+}
+
+impl<T> AtomicUniqueRefCell<T> {
+    pub fn new(value: T) -> Self {
+        Self {
+            count: AtomicBool::new(false),
+            inner: UnsafeCell::new(value),
+        }
+    }
+}
+
+impl<T: ?Sized> AtomicUniqueRefCell<T> {
+    pub fn borrow(&self) -> Ref<'_, T> {
+        if self.count.swap(true, Ordering::Acquire) {
+            panic!("Already borrowed");
+        }
+
+        Ref {
+            inner: self,
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl<T: ?Sized> Deref for Ref<'_, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe { &*self.inner.inner.get() }
+    }
+}
+
+impl<T: ?Sized> DerefMut for Ref<'_, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe { &mut *self.inner.inner.get() }
+    }
+}
+
+impl<T: ?Sized> Drop for Ref<'_, T> {
+    fn drop(&mut self) {
+        self.inner.count.swap(false, Ordering::Release);
+    }
+}

+ 129 - 0
crates/buddy_allocator/Cargo.lock

@@ -0,0 +1,129 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "arch"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "eonix_mm",
+ "percpu-macros",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "bitflags"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+
+[[package]]
+name = "buddy_allocator"
+version = "0.1.0"
+dependencies = [
+ "eonix_mm",
+ "eonix_sync",
+ "intrusive_list",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "eonix_mm"
+version = "0.1.0"
+dependencies = [
+ "intrusive_list",
+]
+
+[[package]]
+name = "eonix_preempt"
+version = "0.1.0"
+dependencies = [
+ "arch",
+]
+
+[[package]]
+name = "eonix_sync"
+version = "0.1.0"
+dependencies = [
+ "arch",
+ "eonix_preempt",
+ "intrusive-collections",
+]
+
+[[package]]
+name = "intrusive-collections"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86"
+dependencies = [
+ "memoffset",
+]
+
+[[package]]
+name = "intrusive_list"
+version = "0.1.0"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "percpu-macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"

+ 8 - 0
crates/buddy_allocator/Cargo.toml

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

+ 59 - 0
crates/buddy_allocator/src/free_area.rs

@@ -0,0 +1,59 @@
+use crate::BuddyRawPage;
+use core::marker::{PhantomData, Send, Sync};
+use intrusive_list::Link;
+
+pub struct FreeArea<T> {
+    free_list: Link,
+    count: usize,
+    _phantom: PhantomData<T>,
+}
+
+unsafe impl<T> Send for FreeArea<T> {}
+unsafe impl<T> Sync for FreeArea<T> {}
+
+impl<Raw> FreeArea<Raw>
+where
+    Raw: BuddyRawPage,
+{
+    pub const fn new() -> Self {
+        Self {
+            free_list: Link::new(),
+            count: 0,
+            _phantom: PhantomData,
+        }
+    }
+
+    pub fn get_free_pages(&mut self) -> Option<Raw> {
+        self.free_list.next_mut().map(|pages_link| {
+            assert_ne!(self.count, 0);
+
+            let pages_ptr = unsafe {
+                // SAFETY: Items in `self.free_list` are guaranteed to be of type `Raw`.
+                Raw::from_link(pages_link)
+            };
+
+            self.count -= 1;
+            pages_link.remove();
+
+            pages_ptr
+        })
+    }
+
+    pub fn add_pages(&mut self, pages_ptr: Raw) {
+        self.count += 1;
+        pages_ptr.set_free();
+
+        unsafe {
+            self.free_list.insert(pages_ptr.get_link());
+        }
+    }
+
+    pub fn del_pages(&mut self, pages_ptr: Raw) {
+        assert!(self.count >= 1 && pages_ptr.is_free());
+        self.count -= 1;
+        pages_ptr.clear_free();
+        unsafe {
+            pages_ptr.get_link().remove();
+        }
+    }
+}

+ 97 - 0
crates/buddy_allocator/src/lib.rs

@@ -0,0 +1,97 @@
+#![no_std]
+
+mod free_area;
+mod zone;
+
+use core::sync::atomic::Ordering;
+use eonix_mm::{
+    address::PAddr,
+    paging::{RawPage, PFN},
+};
+use intrusive_list::Link;
+use zone::Zone;
+
+const MAX_ORDER: u32 = 10;
+const ZONE_AREAS: usize = const { MAX_ORDER as usize + 1 };
+
+pub trait BuddyRawPage: RawPage {
+    /// Get the container raw page struct of the list link.
+    ///
+    /// # Safety
+    /// The caller MUST ensure that the link points to a `RawPage`.
+    unsafe fn from_link(link: &mut Link) -> Self;
+
+    /// Get the list link of the raw page.
+    ///
+    /// # Safety
+    /// The caller MUST ensure that at any time, only one mutable reference
+    /// to the link exists.
+    unsafe fn get_link(&self) -> &mut Link;
+
+    fn set_order(&self, order: u32);
+
+    fn is_buddy(&self) -> bool;
+    fn is_free(&self) -> bool;
+
+    fn set_buddy(&self);
+    fn set_free(&self);
+
+    fn clear_buddy(&self);
+    fn clear_free(&self);
+}
+
+pub struct BuddyAllocator<T>
+where
+    T: BuddyRawPage,
+{
+    zone: Zone<T, ZONE_AREAS>,
+}
+
+impl<T> BuddyAllocator<T>
+where
+    T: BuddyRawPage,
+{
+    pub const fn new() -> Self {
+        Self { zone: Zone::new() }
+    }
+
+    pub fn create_pages(&mut self, start: PAddr, end: PAddr) {
+        self.zone.create_pages(start, end);
+    }
+
+    pub fn alloc_order(&mut self, order: u32) -> Option<T> {
+        let pages_ptr = self.zone.get_free_pages(order);
+
+        if let Some(pages_ptr) = pages_ptr {
+            // SAFETY: Memory order here can be Relaxed is for the same reason as that
+            // in the copy constructor of `std::shared_ptr`.
+            pages_ptr.refcount().fetch_add(1, Ordering::Relaxed);
+            pages_ptr.clear_free();
+        }
+
+        pages_ptr
+    }
+
+    pub unsafe fn dealloc(&mut self, page_ptr: T) {
+        self.zone.free_pages(page_ptr);
+    }
+
+    pub fn has_management_over(page_ptr: T) -> bool {
+        !page_ptr.is_free() && page_ptr.is_buddy()
+    }
+}
+
+pub(self) trait BuddyPFNOps {
+    fn buddy_pfn(self, order: u32) -> PFN;
+    fn combined_pfn(self, buddy_pfn: PFN) -> PFN;
+}
+
+impl BuddyPFNOps for PFN {
+    fn buddy_pfn(self, order: u32) -> PFN {
+        PFN::from(usize::from(self) ^ (1 << order))
+    }
+
+    fn combined_pfn(self, buddy_pfn: PFN) -> PFN {
+        PFN::from(usize::from(self) & usize::from(buddy_pfn))
+    }
+}

+ 146 - 0
crates/buddy_allocator/src/zone.rs

@@ -0,0 +1,146 @@
+use super::free_area::FreeArea;
+use crate::{BuddyPFNOps as _, BuddyRawPage};
+use core::sync::atomic::Ordering;
+use eonix_mm::{
+    address::{AddrOps as _, PAddr},
+    paging::PFN,
+};
+
+pub(super) struct Zone<T, const AREAS: usize> {
+    free_areas: [FreeArea<T>; AREAS],
+}
+
+impl<Raw, const AREAS: usize> Zone<Raw, AREAS>
+where
+    Raw: BuddyRawPage,
+{
+    pub const fn new() -> Self {
+        Self {
+            free_areas: [const { FreeArea::new() }; AREAS],
+        }
+    }
+
+    pub fn get_free_pages(&mut self, order: u32) -> Option<Raw> {
+        for current_order in order..AREAS as u32 {
+            let pages_ptr = self.free_areas[current_order as usize].get_free_pages();
+            let Some(pages_ptr) = pages_ptr else { continue };
+
+            pages_ptr.set_order(order);
+
+            if current_order > order {
+                self.expand(pages_ptr, current_order, order);
+            }
+
+            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);
+        }
+        None
+    }
+
+    fn expand(&mut self, pages_ptr: Raw, order: u32, target_order: u32) {
+        let mut offset = 1 << order;
+        let pages_pfn = Into::<PFN>::into(pages_ptr);
+
+        for order in (target_order..order).rev() {
+            offset >>= 1;
+
+            let split_pages_ptr = Raw::from(pages_pfn + offset);
+            split_pages_ptr.set_order(order);
+            split_pages_ptr.set_buddy();
+            self.free_areas[order as usize].add_pages(split_pages_ptr);
+        }
+    }
+
+    pub fn free_pages(&mut self, mut pages_ptr: Raw) {
+        assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
+
+        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);
+
+            if !self.buddy_check(buddy_pages_ptr, current_order) {
+                break;
+            }
+
+            pages_ptr.clear_buddy();
+            buddy_pages_ptr.clear_buddy();
+            self.free_areas[current_order as usize].del_pages(buddy_pages_ptr);
+
+            pages_ptr = Raw::from(pfn.combined_pfn(buddy_pfn));
+            pfn = pfn.combined_pfn(buddy_pfn);
+
+            pages_ptr.set_buddy();
+            current_order += 1;
+        }
+
+        pages_ptr.set_order(current_order);
+        self.free_areas[current_order as usize].add_pages(pages_ptr);
+    }
+
+    /// This function checks whether a page is free && is a buddy
+    /// we can coalesce a page and its buddy if
+    /// - the buddy is valid(present) &&
+    /// - the buddy is right now in free_areas &&
+    /// - a page and its buddy have the same order &&
+    /// - a page and its buddy are in the same zone (on smp systems).
+    fn buddy_check(&self, pages_ptr: Raw, order: u32) -> bool {
+        if !pages_ptr.is_present() {
+            return false;
+        }
+        if !pages_ptr.is_free() {
+            return false;
+        }
+        if pages_ptr.order() != order {
+            return false;
+        }
+
+        assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
+        true
+    }
+
+    /// Only used on buddy initialization
+    pub fn create_pages(&mut self, start: PAddr, end: PAddr) {
+        let mut start_pfn = PFN::from(start.ceil());
+        let end_pfn = PFN::from(end.floor());
+
+        while start_pfn < end_pfn {
+            let mut order = usize::from(start_pfn)
+                .trailing_zeros()
+                .min((AREAS - 1) as u32);
+
+            while start_pfn + (1 << order) as usize > end_pfn {
+                order -= 1;
+            }
+            let page_ptr = Raw::from(start_pfn);
+            page_ptr.set_buddy();
+            self.free_areas[order as usize].add_pages(page_ptr);
+            start_pfn = start_pfn + (1 << order) as usize;
+        }
+    }
+}

+ 29 - 0
crates/eonix_hal/Cargo.toml

@@ -0,0 +1,29 @@
+[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" }
+
+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"
+
+[target.'cfg(target_arch = "riscv64")'.dependencies]
+intrusive_list = { path = "../intrusive_list" }
+buddy_allocator = { path = "../buddy_allocator" }
+sbi = "0.3.0"
+riscv = { version = "0.13.0", features = ["s-mode"] }
+fdt = "0.1"
+bitflags = "2.6.0"
+
+[target.'cfg(target_arch = "loongarch64")'.dependencies]
+loongArch64 = "0.2.5"
+fdt = "0.1"

+ 79 - 0
crates/eonix_hal/build.rs

@@ -0,0 +1,79 @@
+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_riscv64(script: &mut String) -> Result<(), Box<dyn std::error::Error>> {
+    println!("cargo:extra-link-args= --no-check-sections");
+
+    let memory = read_dependent_script("src/arch/riscv64/memory.x")?;
+    let link = read_dependent_script("src/arch/riscv64/link.x")?;
+
+    *script = memory + script;
+    script.push_str(&link);
+
+    Ok(())
+}
+
+fn process_ldscript_loongarch64(script: &mut String) -> Result<(), Box<dyn std::error::Error>> {
+    println!("cargo:extra-link-args= --no-check-sections");
+
+    let memory = read_dependent_script("src/arch/loongarch64/memory.x")?;
+    let link = read_dependent_script("src/arch/loongarch64/link.x")?;
+
+    *script = memory + script;
+    script.push_str(&link);
+
+    Ok(())
+}
+
+fn process_ldscript_x86(script: &mut String) -> Result<(), Box<dyn std::error::Error>> {
+    // Otherwise `bootstrap.rs` might be ignored and not linked in.
+    println!("cargo:extra-link-args=--undefined=move_mbr --no-check-sections");
+
+    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)?;
+        }
+        "riscv64" => {
+            process_ldscript_riscv64(script)?;
+        }
+        "loongarch64" => {
+            process_ldscript_loongarch64(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()`");
+    }
+}

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

@@ -0,0 +1,23 @@
+use bitflags::bitflags;
+use eonix_mm::address::VAddr;
+
+bitflags! {
+    #[derive(Debug)]
+    pub struct PageFaultErrorCode: u32 {
+        const Read = 2;
+        const Write = 4;
+        const InstructionFetch = 8;
+        const UserAccess = 16;
+    }
+}
+
+#[derive(Debug)]
+pub enum Fault {
+    InvalidOp,
+    BadAccess,
+    PageFault {
+        error_code: PageFaultErrorCode,
+        address: VAddr,
+    },
+    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;
+}

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

@@ -0,0 +1,115 @@
+use crate::{context::RawTaskContext, fault::Fault};
+use core::marker::PhantomData;
+use eonix_mm::address::VAddr;
+
+/// 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 {
+    type FIrq: FnOnce(fn(irqno: usize));
+    type FTimer: FnOnce(fn());
+
+    fn new() -> Self;
+
+    fn trap_type(&self) -> TrapType<Self::FIrq, Self::FTimer>;
+
+    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);
+
+    fn set_user_call_frame<E>(
+        &mut self,
+        pc: usize,
+        sp: Option<usize>,
+        ra: Option<usize>,
+        args: &[usize],
+        write_memory: impl Fn(VAddr, &[u8]) -> Result<(), E>,
+    ) -> Result<(), E>;
+}
+
+#[doc(notable_trait)]
+pub trait TrapReturn {
+    type TaskContext: RawTaskContext;
+
+    /// Return to the context before the trap occurred.
+    ///
+    /// # Safety
+    /// 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<FIrq, FTimer>
+where
+    FIrq: FnOnce(fn(irqno: usize)),
+    FTimer: FnOnce(fn()),
+{
+    Syscall { no: usize, args: [usize; 6] },
+    Fault(Fault),
+    Breakpoint,
+    Irq { callback: FIrq },
+    Timer { callback: FTimer },
+}
+
+/// 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;

+ 306 - 0
crates/eonix_hal/src/arch/loongarch64/bootstrap.rs

@@ -0,0 +1,306 @@
+use super::cpu::CPUID;
+use super::cpu::CPU_COUNT;
+use crate::{
+    arch::{
+        cpu::CPU,
+        mm::{ArchPhysAccess, PageAttribute64, GLOBAL_PAGE_TABLE},
+        trap::CSR_KERNEL_TP,
+    },
+    bootstrap::BootStrapData,
+    mm::{
+        flush_tlb_all, ArchMemory, ArchPagingMode, BasicPageAlloc, BasicPageAllocRef,
+        ScopedAllocator,
+    },
+};
+use core::arch::naked_asm;
+use core::{
+    alloc::Allocator,
+    arch::asm,
+    cell::RefCell,
+    sync::atomic::{AtomicBool, AtomicUsize, Ordering},
+};
+use eonix_hal_traits::mm::Memory;
+use eonix_mm::{
+    address::{Addr as _, PAddr, PRange, PhysAccess, VAddr, VRange},
+    page_table::{PageAttribute, PagingMode, PTE as _},
+    paging::{Page, PageAccess, PageAlloc, PAGE_SIZE, PFN},
+};
+use eonix_percpu::PercpuArea;
+use loongArch64::register::ecfg;
+use loongArch64::register::ecfg::LineBasedInterrupt;
+use loongArch64::register::tcfg;
+use loongArch64::register::{euen, pgdl};
+
+#[unsafe(link_section = ".bootstrap.stack")]
+static BOOT_STACK: [u8; 4096 * 16] = [0; 4096 * 16];
+static BOOT_STACK_START: &'static [u8; 4096 * 16] = &BOOT_STACK;
+
+#[repr(C, align(4096))]
+struct PageTable([u64; 512]);
+
+/// map 0x8000_0000 to 0x8000_0000 and 0xffff_ffff_8000_0000
+#[unsafe(link_section = ".bootstrap.page_table.1")]
+static BOOT_PAGE_TABLE: PageTable = {
+    let mut arr = [0; 512];
+    arr[0] = 0x8000_2000 | (1 << 60);
+    arr[510] = 0x8000_2000 | (1 << 60);
+    arr[511] = 0x8000_3000 | (1 << 60);
+
+    PageTable(arr)
+};
+
+#[unsafe(link_section = ".bootstrap.page_table.2")]
+#[used]
+static PT1: PageTable = {
+    let mut arr: [u64; 512] = [0; 512];
+    let mut i: usize = 0;
+    while i < 512 {
+        arr[i] = (i as u64 * 0x4000_0000) | 0x11d3;
+        i += 1;
+    }
+
+    PageTable(arr)
+};
+
+#[unsafe(link_section = ".bootstrap.page_table.3")]
+#[used]
+static PT2: PageTable = {
+    let mut arr = [0; 512];
+    arr[510] = 0x8000_0000 | 0x11d3; // G | W | P | H | Cached | D | V
+
+    PageTable(arr)
+};
+
+/// bootstrap in rust
+#[unsafe(naked)]
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".bootstrap.entry")]
+unsafe extern "C" fn _start() -> ! {
+    naked_asm!(
+        "
+            li.d      $t0, 0xc
+            csrwr     $t0, {CSR_STLB_PAGE_SIZE}
+
+            li.d      $t0, {PWCL}
+            csrwr     $t0, {CSR_PWCL}
+
+            li.d      $t0, {PWCH}
+            csrwr     $t0, {CSR_PWCH}
+
+            la.global $t0, {tlb_refill_entry}
+            csrwr     $t0, {CSR_TLB_REFILL_ENTRY}
+
+            la.global $t0, {page_table}
+            move      $t1, $t0
+            csrwr     $t0, {CSR_PGDL}
+            csrwr     $t1, {CSR_PGDH}
+
+            dbar      0x0
+            invtlb    0x0, $zero, $zero
+
+            csrrd     $t0, {CSR_CRMD}
+            li.d      $t1, ~0x18
+            and       $t0, $t0, $t1
+            ori       $t0, $t0,  0x10
+            csrwr     $t0, {CSR_CRMD}
+
+            la.global $sp, {boot_stack}
+            li.d      $t0, 0xffffff0000000000
+            or        $sp, $sp, $t0
+            li.d      $t0, {BOOT_STACK_SIZE}
+            add.d     $sp, $sp, $t0
+
+            csrrd     $a0, {CSR_CPUID}
+            move      $ra, $zero
+
+            la.global $t0, {riscv64_start}
+            jirl      $zero, $t0, 0
+        ",
+        boot_stack = sym BOOT_STACK,
+        BOOT_STACK_SIZE = const size_of_val(&BOOT_STACK),
+        CSR_CRMD = const 0x00,
+        CSR_PGDL = const 0x19,
+        CSR_PGDH = const 0x1a,
+        CSR_PWCL = const 0x1c,
+        CSR_PWCH = const 0x1d,
+        CSR_STLB_PAGE_SIZE = const 0x1e,
+        CSR_CPUID = const 0x20,
+        CSR_TLB_REFILL_ENTRY = const 0x88,
+        PWCL = const (12 << 0) | (9 << 5) | (21 << 10) | (9 << 15) | (30 << 20) | (9 << 25) | (0 << 30),
+        PWCH = const (39 << 0) | (9 << 6),
+        tlb_refill_entry = sym tlb_refill_entry,
+        page_table = sym BOOT_PAGE_TABLE,
+        riscv64_start = sym riscv64_start,
+    )
+}
+
+#[unsafe(naked)]
+#[unsafe(link_section = ".bootstrap.tlb_fill_entry")]
+unsafe extern "C" fn tlb_refill_entry() {
+    naked_asm!(
+        "csrwr   $t0, {CSR_TLBRSAVE}",
+        "csrrd   $t0, {CSR_PGD}",
+        "lddir   $t0, $t0, 3",
+        "lddir   $t0, $t0, 2",
+        "lddir   $t0, $t0, 1",
+        "ldpte   $t0, 0",
+        "ldpte   $t0, 1",
+        "tlbfill",
+        "csrrd   $t0, {CSR_TLBRSAVE}",
+        "ertn",
+        CSR_TLBRSAVE = const 0x8b,
+        CSR_PGD = const 0x1b,
+    )
+}
+
+/// TODO:
+/// 启动所有的cpu
+pub unsafe extern "C" fn riscv64_start(hart_id: usize) -> ! {
+    pgdl::set_base(0xffff_ffff_ffff_0000);
+    flush_tlb_all();
+
+    let real_allocator = RefCell::new(BasicPageAlloc::new());
+    let alloc = BasicPageAllocRef::new(&real_allocator);
+
+    for range in ArchMemory::free_ram() {
+        real_allocator.borrow_mut().add_range(range);
+    }
+
+    setup_kernel_page_table(&alloc);
+
+    setup_cpu(&alloc, hart_id);
+
+    // TODO: set up interrupt, smp
+    ScopedAllocator::new(&mut [0; 1024])
+        .with_alloc(|mem_alloc| bootstrap_smp(mem_alloc, &real_allocator));
+
+    unsafe extern "Rust" {
+        fn _eonix_hal_main(_: BootStrapData) -> !;
+    }
+
+    let start = unsafe {
+        ((&BOOT_STACK_START) as *const &'static [u8; 4096 * 16]).read_volatile() as *const _
+            as usize
+    };
+    let bootstrap_data = BootStrapData {
+        early_stack: PRange::new(
+            PAddr::from(start),
+            PAddr::from(start + size_of_val(&BOOT_STACK)),
+        ),
+        allocator: Some(real_allocator),
+    };
+
+    unsafe {
+        _eonix_hal_main(bootstrap_data);
+    }
+}
+
+unsafe extern "C" {
+    fn BSS_LENGTH();
+    fn KIMAGE_PAGES();
+}
+
+fn setup_kernel_page_table(alloc: impl PageAlloc) {
+    let global_page_table = &GLOBAL_PAGE_TABLE;
+
+    let attr = PageAttribute::WRITE
+        | PageAttribute::READ
+        | PageAttribute::EXECUTE
+        | PageAttribute::GLOBAL
+        | PageAttribute::PRESENT
+        | PageAttribute::ACCESSED
+        | PageAttribute::DIRTY;
+
+    const KERNEL_BSS_START: VAddr = VAddr::from(0xffffffff40000000);
+
+    // Map kernel BSS
+    for pte in global_page_table.iter_kernel_in(
+        VRange::from(KERNEL_BSS_START).grow(BSS_LENGTH as usize),
+        ArchPagingMode::LEVELS,
+        &alloc,
+    ) {
+        let page = Page::alloc_in(&alloc);
+
+        let attr = {
+            let mut attr = attr.clone();
+            attr.remove(PageAttribute::EXECUTE);
+            attr
+        };
+        pte.set(page.into_raw(), attr.into());
+    }
+
+    flush_tlb_all();
+
+    unsafe {
+        core::ptr::write_bytes(KERNEL_BSS_START.addr() as *mut (), 0, BSS_LENGTH as usize);
+    }
+}
+
+/// set up tp register to percpu
+fn setup_cpu(alloc: impl PageAlloc, hart_id: usize) {
+    // enable FPU
+    euen::set_fpe(true);
+    euen::set_sxe(true);
+
+    CPU_COUNT.fetch_add(1, Ordering::Relaxed);
+
+    let mut percpu_area = PercpuArea::new(|layout| {
+        let page_count = layout.size().div_ceil(PAGE_SIZE);
+        let page = Page::alloc_at_least_in(page_count, alloc);
+
+        let ptr = ArchPhysAccess::get_ptr_for_page(&page).cast();
+        page.into_raw();
+
+        ptr
+    });
+
+    // set tp(x4) register
+    percpu_area.setup(|pointer| {
+        let percpu_base_addr = pointer.addr().get();
+        unsafe {
+            asm!(
+                "move $tp, {0}",
+                in(reg) percpu_base_addr,
+                options(nostack, preserves_flags)
+            );
+        }
+    });
+
+    CPUID.set(hart_id);
+
+    let mut cpu = CPU::local();
+    unsafe {
+        cpu.as_mut().init();
+    }
+
+    percpu_area.register(cpu.cpuid());
+
+    unsafe {
+        asm!(
+            "csrwr {tp}, {CSR_KERNEL_TP}",
+            tp = inout(reg) PercpuArea::get_for(cpu.cpuid()).unwrap().as_ptr() => _,
+            CSR_KERNEL_TP = const CSR_KERNEL_TP,
+        )
+    }
+
+    let timer_frequency = loongArch64::time::get_timer_freq();
+
+    // 1ms periodic timer.
+    tcfg::set_init_val(timer_frequency / 1_000);
+    tcfg::set_periodic(true);
+    tcfg::set_en(true);
+
+    ecfg::set_lie(LineBasedInterrupt::all());
+}
+
+/// TODO
+fn bootstrap_smp(alloc: impl Allocator, page_alloc: &RefCell<BasicPageAlloc>) {}
+
+pub fn shutdown() -> ! {
+    let ged_addr = PAddr::from(0x100E001C);
+    unsafe {
+        let ged_ptr = ArchPhysAccess::as_ptr::<u8>(ged_addr);
+        ged_ptr.write_volatile(0x34);
+        loop {}
+    }
+}

+ 110 - 0
crates/eonix_hal/src/arch/loongarch64/context.rs

@@ -0,0 +1,110 @@
+use core::arch::naked_asm;
+use eonix_hal_traits::context::RawTaskContext;
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct TaskContext {
+    sx: [u64; 9],
+    sp: u64,
+    ra: u64,
+    fp: u64,
+    crmd: usize,
+}
+
+impl RawTaskContext for TaskContext {
+    fn new() -> Self {
+        Self::new()
+    }
+
+    fn set_program_counter(&mut self, pc: usize) {
+        self.ra = pc as u64;
+    }
+
+    fn set_stack_pointer(&mut self, sp: usize) {
+        self.sp = sp as u64;
+    }
+
+    fn is_interrupt_enabled(&self) -> bool {
+        self.crmd & (1 << 2) != 0
+    }
+
+    fn set_interrupt_enabled(&mut self, is_enabled: bool) {
+        if is_enabled {
+            self.crmd = self.crmd | (1 << 2);
+        } else {
+            self.crmd = self.crmd & !(1 << 2);
+        }
+    }
+
+    fn call(&mut self, func: unsafe extern "C" fn(usize) -> !, arg: usize) {
+        self.sx[0] = func as u64;
+        self.sx[1] = arg as u64;
+
+        self.set_program_counter(Self::do_call as usize);
+    }
+
+    #[unsafe(naked)]
+    unsafe extern "C" fn switch(from: &mut Self, to: &mut Self) {
+        // Input arguments `from` and `to` will be in `a0` (x10) and `a1` (x11).
+        naked_asm!(
+            // Save current task's callee-saved registers to `from` context
+            "st.d  $s0, $a0,  0",
+            "st.d  $s1, $a0,  8",
+            "st.d  $s2, $a0, 16",
+            "st.d  $s3, $a0, 24",
+            "st.d  $s4, $a0, 32",
+            "st.d  $s5, $a0, 40",
+            "st.d  $s6, $a0, 48",
+            "st.d  $s7, $a0, 56",
+            "st.d  $s8, $a0, 64",
+            "st.d  $sp, $a0, 72",
+            "st.d  $ra, $a0, 80",
+            "st.d  $fp, $a0, 88",
+            "csrrd $t0, 0", // CRMD
+            "st.d  $t0, $a0, 96",
+            "",
+            "ld.d  $s0, $a1,  0",
+            "ld.d  $s1, $a1,  8",
+            "ld.d  $s2, $a1, 16",
+            "ld.d  $s3, $a1, 24",
+            "ld.d  $s4, $a1, 32",
+            "ld.d  $s5, $a1, 40",
+            "ld.d  $s6, $a1, 48",
+            "ld.d  $s7, $a1, 56",
+            "ld.d  $s8, $a1, 64",
+            "ld.d  $sp, $a1, 72",
+            "ld.d  $ra, $a1, 80",
+            "ld.d  $fp, $a1, 88",
+            "ld.d  $t0, $a1, 96",
+            "csrwr $t0, 0", // CRMD
+            "ret",
+        );
+    }
+}
+
+impl TaskContext {
+    pub const fn new() -> Self {
+        Self {
+            sx: [0; 9],
+            sp: 0,
+            ra: 0,
+            fp: 0,
+            crmd: 1 << 4, // PG = 1
+        }
+    }
+
+    #[unsafe(naked)]
+    /// Maximum of 5 arguments supported.
+    unsafe extern "C" fn do_call() -> ! {
+        naked_asm!(
+            "move $a0, $s1", // Args
+            "move $a1, $s2",
+            "move $a2, $s3",
+            "move $a3, $s4",
+            "move $a4, $s5",
+            "move $fp, $zero", // Set frame pointer to 0.
+            "move $ra, $zero",
+            "jirl $zero, $s0, 0",
+        );
+    }
+}

+ 72 - 0
crates/eonix_hal/src/arch/loongarch64/cpu.rs

@@ -0,0 +1,72 @@
+use super::trap::setup_trap;
+use core::sync::atomic::AtomicUsize;
+use core::{arch::asm, pin::Pin, ptr::NonNull};
+use eonix_preempt::PreemptGuard;
+use eonix_sync_base::LazyLock;
+
+pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(0);
+
+#[eonix_percpu::define_percpu]
+pub static CPUID: usize = 0;
+
+#[eonix_percpu::define_percpu]
+static LOCAL_CPU: LazyLock<CPU> = LazyLock::new(|| CPU::new(CPUID.get()));
+
+#[derive(Debug, Clone)]
+pub enum UserTLS {
+    Base(u64),
+}
+
+pub struct CPU {}
+
+impl UserTLS {
+    pub fn new(base: u64) -> Self {
+        Self::Base(base)
+    }
+}
+
+impl CPU {
+    fn new(cpuid: usize) -> Self {
+        Self {}
+    }
+
+    /// Load CPU specific configurations for the current Hart.
+    ///
+    /// # Safety
+    /// This function performs low-level hardware initialization and should
+    /// only be called once per Hart during its boot sequence.
+    pub unsafe fn init(mut self: Pin<&mut Self>) {
+        let me = self.as_mut().get_unchecked_mut();
+        setup_trap();
+    }
+
+    /// Boot all other hart.
+    pub unsafe fn bootstrap_cpus(&self) {
+        // todo
+    }
+
+    pub unsafe fn load_interrupt_stack(self: Pin<&mut Self>, _: u64) {}
+
+    pub fn set_tls32(self: Pin<&mut Self>, _user_tls: &UserTLS) {
+        // nothing
+    }
+
+    pub fn local() -> PreemptGuard<Pin<&'static mut Self>> {
+        unsafe {
+            // SAFETY: We pass the reference into a `PreemptGuard`, which ensures
+            //         that preemption is disabled.
+            PreemptGuard::new(Pin::new_unchecked(LOCAL_CPU.as_mut().get_mut()))
+        }
+    }
+
+    pub fn cpuid(&self) -> usize {
+        CPUID.get()
+    }
+}
+
+#[inline(always)]
+pub fn halt() {
+    unsafe {
+        loongArch64::asm::idle();
+    }
+}

+ 32 - 0
crates/eonix_hal/src/arch/loongarch64/fdt.rs

@@ -0,0 +1,32 @@
+use super::mm::ArchPhysAccess;
+use core::sync::atomic::{AtomicPtr, Ordering};
+use eonix_mm::address::{Addr, PAddr, PRange, PhysAccess};
+use eonix_sync_base::LazyLock;
+use fdt::Fdt;
+
+const FDT_PADDR: PAddr = PAddr::from_val(0x100000);
+
+pub static FDT: LazyLock<Fdt<'static>> = LazyLock::new(|| unsafe {
+    Fdt::from_ptr(ArchPhysAccess::as_ptr(FDT_PADDR).as_ptr())
+        .expect("Failed to parse DTB from static memory.")
+});
+
+pub trait FdtExt {
+    fn harts(&self) -> impl Iterator<Item = usize>;
+
+    fn hart_count(&self) -> usize {
+        self.harts().count()
+    }
+
+    fn present_ram(&self) -> impl Iterator<Item = PRange>;
+}
+
+impl FdtExt for Fdt<'_> {
+    fn harts(&self) -> impl Iterator<Item = usize> {
+        self.cpus().map(|cpu| cpu.ids().all()).flatten()
+    }
+
+    fn present_ram(&self) -> impl Iterator<Item = PRange> {
+        core::iter::empty()
+    }
+}

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

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

+ 141 - 0
crates/eonix_hal/src/arch/loongarch64/fpu.rs

@@ -0,0 +1,141 @@
+use core::arch::asm;
+use eonix_hal_traits::fpu::RawFpuState;
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+pub struct FpuState {
+    pub fx: [u64; 32],
+    pub fcc: u64,
+    pub fcsr: u64,
+}
+
+impl RawFpuState for FpuState {
+    fn new() -> Self {
+        unsafe { core::mem::zeroed() }
+    }
+
+    /// Save reg -> mem
+    fn save(&mut self) {
+        unsafe {
+            asm!(
+            "fst.d $f0,  {base},  0 * 8",
+            "fst.d $f1,  {base},  1 * 8",
+            "fst.d $f2,  {base},  2 * 8",
+            "fst.d $f3,  {base},  3 * 8",
+            "fst.d $f4,  {base},  4 * 8",
+            "fst.d $f5,  {base},  5 * 8",
+            "fst.d $f6,  {base},  6 * 8",
+            "fst.d $f7,  {base},  7 * 8",
+            "fst.d $f8,  {base},  8 * 8",
+            "fst.d $f9,  {base},  9 * 8",
+            "fst.d $f10, {base}, 10 * 8",
+            "fst.d $f11, {base}, 11 * 8",
+            "fst.d $f12, {base}, 12 * 8",
+            "fst.d $f13, {base}, 13 * 8",
+            "fst.d $f14, {base}, 14 * 8",
+            "fst.d $f15, {base}, 15 * 8",
+            "fst.d $f16, {base}, 16 * 8",
+            "fst.d $f17, {base}, 17 * 8",
+            "fst.d $f18, {base}, 18 * 8",
+            "fst.d $f19, {base}, 19 * 8",
+            "fst.d $f20, {base}, 20 * 8",
+            "fst.d $f21, {base}, 21 * 8",
+            "fst.d $f22, {base}, 22 * 8",
+            "fst.d $f23, {base}, 23 * 8",
+            "fst.d $f24, {base}, 24 * 8",
+            "fst.d $f25, {base}, 25 * 8",
+            "fst.d $f26, {base}, 26 * 8",
+            "fst.d $f27, {base}, 27 * 8",
+            "fst.d $f28, {base}, 28 * 8",
+            "fst.d $f29, {base}, 29 * 8",
+            "fst.d $f30, {base}, 30 * 8",
+            "fst.d $f31, {base}, 31 * 8",
+            "",
+            "movcf2gr    {tmp}, $fcc0",
+            "move        {fcc}, {tmp}",
+            "movcf2gr    {tmp}, $fcc1",
+            "bstrins.d   {fcc}, {tmp}, 15, 8",
+            "movcf2gr    {tmp}, $fcc2",
+            "bstrins.d   {fcc}, {tmp}, 23, 16",
+            "movcf2gr    {tmp}, $fcc3",
+            "bstrins.d   {fcc}, {tmp}, 31, 24",
+            "movcf2gr    {tmp}, $fcc4",
+            "bstrins.d   {fcc}, {tmp}, 39, 32",
+            "movcf2gr    {tmp}, $fcc5",
+            "bstrins.d   {fcc}, {tmp}, 47, 40",
+            "movcf2gr    {tmp}, $fcc6",
+            "bstrins.d   {fcc}, {tmp}, 55, 48",
+            "movcf2gr    {tmp}, $fcc7",
+            "bstrins.d   {fcc}, {tmp}, 63, 56",
+            "",
+            "movfcsr2gr  {fcsr}, $fcsr0",
+            base = in(reg) &raw mut self.fx,
+            tmp = out(reg) _,
+            fcc = out(reg) self.fcc,
+            fcsr = out(reg) self.fcsr,
+            options(nostack, preserves_flags));
+        }
+    }
+
+    fn restore(&mut self) {
+        unsafe {
+            asm!(
+            "fld.d $f0,  {base},  0 * 8",
+            "fld.d $f1,  {base},  1 * 8",
+            "fld.d $f2,  {base},  2 * 8",
+            "fld.d $f3,  {base},  3 * 8",
+            "fld.d $f4,  {base},  4 * 8",
+            "fld.d $f5,  {base},  5 * 8",
+            "fld.d $f6,  {base},  6 * 8",
+            "fld.d $f7,  {base},  7 * 8",
+            "fld.d $f8,  {base},  8 * 8",
+            "fld.d $f9,  {base},  9 * 8",
+            "fld.d $f10, {base}, 10 * 8",
+            "fld.d $f11, {base}, 11 * 8",
+            "fld.d $f12, {base}, 12 * 8",
+            "fld.d $f13, {base}, 13 * 8",
+            "fld.d $f14, {base}, 14 * 8",
+            "fld.d $f15, {base}, 15 * 8",
+            "fld.d $f16, {base}, 16 * 8",
+            "fld.d $f17, {base}, 17 * 8",
+            "fld.d $f18, {base}, 18 * 8",
+            "fld.d $f19, {base}, 19 * 8",
+            "fld.d $f20, {base}, 20 * 8",
+            "fld.d $f21, {base}, 21 * 8",
+            "fld.d $f22, {base}, 22 * 8",
+            "fld.d $f23, {base}, 23 * 8",
+            "fld.d $f24, {base}, 24 * 8",
+            "fld.d $f25, {base}, 25 * 8",
+            "fld.d $f26, {base}, 26 * 8",
+            "fld.d $f27, {base}, 27 * 8",
+            "fld.d $f28, {base}, 28 * 8",
+            "fld.d $f29, {base}, 29 * 8",
+            "fld.d $f30, {base}, 30 * 8",
+            "fld.d $f31, {base}, 31 * 8",
+            "",
+            "bstrpick.d  {tmp}, {fcc}, 7, 0",
+            "movgr2cf    $fcc0, {tmp}",
+            "bstrpick.d  {tmp}, {fcc}, 15, 8",
+            "movgr2cf    $fcc1, {tmp}",
+            "bstrpick.d  {tmp}, {fcc}, 23, 16",
+            "movgr2cf    $fcc2, {tmp}",
+            "bstrpick.d  {tmp}, {fcc}, 31, 24",
+            "movgr2cf    $fcc3, {tmp}",
+            "bstrpick.d  {tmp}, {fcc}, 39, 32",
+            "movgr2cf    $fcc4, {tmp}",
+            "bstrpick.d  {tmp}, {fcc}, 47, 40",
+            "movgr2cf    $fcc5, {tmp}",
+            "bstrpick.d  {tmp}, {fcc}, 55, 48",
+            "movgr2cf    $fcc6, {tmp}",
+            "bstrpick.d  {tmp}, {fcc}, 63, 56",
+            "movgr2cf    $fcc7, {tmp}",
+            "",
+            "movgr2fcsr $fcsr0, {fcsr}",
+            base = in(reg) &raw mut self.fx,
+            fcc = in(reg) self.fcc,
+            fcsr = in(reg) self.fcsr,
+            tmp = out(reg) _,
+            options(nostack, preserves_flags));
+        }
+    }
+}

+ 97 - 0
crates/eonix_hal/src/arch/loongarch64/link.x

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

+ 21 - 0
crates/eonix_hal/src/arch/loongarch64/memory.x

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

+ 318 - 0
crates/eonix_hal/src/arch/loongarch64/mm.rs

@@ -0,0 +1,318 @@
+use crate::traits::mm::Memory;
+use core::{
+    arch::asm,
+    marker::PhantomData,
+    ptr::NonNull,
+    sync::atomic::{compiler_fence, Ordering},
+};
+use eonix_mm::{
+    address::{Addr as _, AddrOps, PAddr, PRange, PhysAccess, VAddr},
+    page_table::{
+        PageAttribute, PageTable, PageTableLevel, PagingMode, RawAttribute, RawPageTable,
+        TableAttribute, PTE,
+    },
+    paging::{NoAlloc, Page, PageBlock, PAGE_SIZE, PFN},
+};
+use eonix_sync_base::LazyLock;
+use loongArch64::register::pgdl;
+
+pub const KIMAGE_OFFSET: usize = 0xffff_ffff_0000_0000;
+pub const ROOT_PAGE_TABLE_PFN: usize = 0x8000_1000 >> 12;
+pub const PAGE_TABLE_BASE: PFN = PFN::from_val(ROOT_PAGE_TABLE_PFN);
+pub static GLOBAL_PAGE_TABLE: LazyLock<PageTable<ArchPagingMode, NoAlloc, ArchPhysAccess>> =
+    LazyLock::new(|| unsafe {
+        Page::with_raw(PAGE_TABLE_BASE, |root_table_page| {
+            PageTable::with_root_table(root_table_page.clone())
+        })
+    });
+
+pub const PA_VP: u64 = ((1 << 0) | (1 << 7));
+pub const PA_D: u64 = 1 << 1;
+pub const PA_U: u64 = 3 << 2;
+pub const PA_CACHED: u64 = 1 << 4;
+pub const PA_G: u64 = 1 << 6;
+pub const PA_W: u64 = 1 << 8;
+pub const PA_NR: u64 = 1 << 61;
+pub const PA_NX: u64 = 1 << 62;
+
+// in RSW
+pub const PA_COW: u64 = 1 << 9;
+pub const PA_MMAP: u64 = 1 << 10;
+
+pub const PA_PT_USER: u64 = 1 << 59;
+pub const PA_PT: u64 = 1 << 60;
+
+pub const PA_FLAGS_MASK: u64 = 0xF800_0000_0000_0FFF;
+
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct PTE64(u64);
+
+#[derive(Clone, Copy)]
+pub struct PageAttribute64(u64);
+
+pub struct RawPageTable48<'a>(NonNull<PTE64>, PhantomData<&'a ()>);
+
+pub struct PagingMode48;
+
+pub struct ArchPhysAccess;
+
+pub struct ArchMemory;
+
+impl PTE for PTE64 {
+    type Attr = PageAttribute64;
+
+    fn set(&mut self, pfn: PFN, attr: Self::Attr) {
+        let pfn = ((usize::from(pfn) as u64) << 12) & !PA_FLAGS_MASK;
+        self.0 = pfn | attr.0;
+    }
+
+    fn get(&self) -> (PFN, Self::Attr) {
+        let pfn = PFN::from((self.0 & !PA_FLAGS_MASK) as usize >> 12);
+        let attr = PageAttribute64(self.0 & PA_FLAGS_MASK);
+        (pfn, attr)
+    }
+}
+
+impl PagingMode for PagingMode48 {
+    type Entry = PTE64;
+    type RawTable<'a> = RawPageTable48<'a>;
+    const LEVELS: &'static [PageTableLevel] = &[
+        PageTableLevel::new(39, 9),
+        PageTableLevel::new(30, 9),
+        PageTableLevel::new(21, 9),
+        PageTableLevel::new(12, 9),
+    ];
+}
+
+pub type ArchPagingMode = PagingMode48;
+
+unsafe impl Send for RawPageTable48<'_> {}
+
+impl<'a> RawPageTable<'a> for RawPageTable48<'a> {
+    type Entry = PTE64;
+
+    fn index(&self, index: u16) -> &'a Self::Entry {
+        unsafe { self.0.add(index as usize).as_ref() }
+    }
+
+    fn index_mut(&mut self, index: u16) -> &'a mut Self::Entry {
+        unsafe { self.0.add(index as usize).as_mut() }
+    }
+
+    unsafe fn from_ptr(ptr: NonNull<PageBlock>) -> Self {
+        Self(ptr.cast(), PhantomData)
+    }
+}
+
+impl RawAttribute for PageAttribute64 {
+    fn null() -> Self {
+        Self(0)
+    }
+
+    fn as_table_attr(self) -> Option<TableAttribute> {
+        let mut table_attr = TableAttribute::empty();
+
+        if self.0 & PA_PT == PA_PT {
+            table_attr |= TableAttribute::PRESENT;
+        }
+
+        if self.0 & PA_PT_USER == PA_PT_USER {
+            table_attr |= TableAttribute::USER;
+        }
+
+        Some(table_attr)
+    }
+
+    fn as_page_attr(self) -> Option<PageAttribute> {
+        let mut page_attr = PageAttribute::empty();
+
+        if self.0 & PA_PT == PA_PT {
+            return None;
+        }
+
+        if self.0 & PA_VP == PA_VP {
+            page_attr |= PageAttribute::PRESENT;
+        }
+
+        if self.0 & PA_NR == 0 {
+            page_attr |= PageAttribute::READ;
+        }
+
+        if self.0 & PA_W != 0 {
+            page_attr |= PageAttribute::WRITE;
+        }
+
+        if self.0 & PA_NX == 0 {
+            page_attr |= PageAttribute::EXECUTE;
+        }
+
+        if self.0 & PA_U == PA_U {
+            page_attr |= PageAttribute::USER;
+        }
+
+        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;
+        }
+
+        Some(page_attr)
+    }
+}
+
+impl From<PageAttribute> for PageAttribute64 {
+    fn from(page_attr: PageAttribute) -> Self {
+        let mut raw_attr = PA_NR | PA_NX | PA_CACHED;
+
+        for attr in page_attr.iter() {
+            match attr {
+                PageAttribute::PRESENT => raw_attr |= PA_VP,
+                PageAttribute::READ => raw_attr &= !PA_NR,
+                PageAttribute::WRITE => raw_attr |= PA_W,
+                PageAttribute::EXECUTE => raw_attr &= !PA_NX,
+                PageAttribute::USER => raw_attr |= PA_U,
+                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::ACCESSED | PageAttribute::ANONYMOUS => {}
+                _ => unreachable!("Invalid page attribute"),
+            }
+        }
+
+        Self(raw_attr)
+    }
+}
+
+impl From<TableAttribute> for PageAttribute64 {
+    fn from(table_attr: TableAttribute) -> Self {
+        let mut raw_attr = 0;
+
+        for attr in table_attr.iter() {
+            match attr {
+                TableAttribute::PRESENT => raw_attr |= PA_PT,
+                TableAttribute::USER => raw_attr |= PA_PT_USER,
+                TableAttribute::GLOBAL | TableAttribute::ACCESSED => {}
+                _ => unreachable!("Invalid table attribute"),
+            }
+        }
+
+        Self(raw_attr)
+    }
+}
+
+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 Memory for ArchMemory {
+    fn present_ram() -> impl Iterator<Item = PRange> {
+        let range1 = core::iter::once(PRange::from(PAddr::from_val(0)).grow(0x1000_0000));
+        let range2 = core::iter::once(PRange::from(PAddr::from_val(0x8000_0000)).grow(0x3000_0000));
+
+        range2.chain(range1)
+    }
+
+    fn free_ram() -> impl Iterator<Item = PRange> {
+        unsafe extern "C" {
+            fn __kernel_start();
+            fn __kernel_end();
+        }
+
+        let kernel_start = PAddr::from(__kernel_start as usize);
+        let kernel_end = PAddr::from(__kernel_end as usize);
+        let paddr_after_kimage_aligned = kernel_end.ceil_to(PAGE_SIZE);
+
+        Self::present_ram()
+            .filter(move |range| {
+                range.end() <= kernel_start || range.end() > paddr_after_kimage_aligned
+            })
+            .map(move |range| {
+                if range.end() > paddr_after_kimage_aligned
+                    && range.start() < paddr_after_kimage_aligned
+                {
+                    let (_, right) = range.split_at(paddr_after_kimage_aligned);
+                    right
+                } else {
+                    range
+                }
+            })
+    }
+}
+
+pub type DefaultPagingMode = PagingMode48;
+
+#[inline(always)]
+pub fn flush_tlb(vaddr: usize) {
+    unsafe {
+        asm!(
+            "dbar 0x0",
+            "invtlb 0x5, $zero, {vaddr}",
+            vaddr = in(reg) vaddr,
+        );
+    }
+}
+
+#[inline(always)]
+pub fn flush_tlb_all() {
+    unsafe {
+        asm!("dbar 0x0", "invtlb 0x0, $zero, $zero");
+    }
+}
+
+#[inline(always)]
+pub fn get_root_page_table_pfn() -> PFN {
+    PFN::from(PAddr::from(pgdl::read().base()))
+}
+
+#[inline(always)]
+pub fn set_root_page_table_pfn(pfn: PFN) {
+    compiler_fence(Ordering::SeqCst);
+
+    unsafe {
+        pgdl::set_base(PAddr::from(pfn).addr());
+    }
+
+    compiler_fence(Ordering::SeqCst);
+
+    // Invalidate all user space TLB entries.
+    unsafe {
+        asm!("dbar 0x0", "invtlb 0x0, $zero, $zero");
+    }
+}

+ 8 - 0
crates/eonix_hal/src/arch/loongarch64/mod.rs

@@ -0,0 +1,8 @@
+pub mod bootstrap;
+pub mod context;
+pub mod cpu;
+pub mod fdt;
+pub mod fence;
+pub mod fpu;
+pub mod mm;
+pub mod trap;

+ 357 - 0
crates/eonix_hal/src/arch/loongarch64/trap/mod.rs

@@ -0,0 +1,357 @@
+mod trap_context;
+
+use super::context::TaskContext;
+use core::arch::{asm, global_asm, naked_asm};
+use core::cell::UnsafeCell;
+use core::mem::{offset_of, size_of};
+use core::num::NonZero;
+use core::ptr::NonNull;
+use eonix_hal_traits::{
+    context::RawTaskContext,
+    trap::{IrqState as IrqStateTrait, TrapReturn},
+};
+use loongArch64::register::crmd::{self, Crmd};
+use loongArch64::register::ecfg;
+use loongArch64::register::eentry::{self, Eentry};
+
+pub use trap_context::*;
+
+pub const CSR_KERNEL_TP: usize = 0x30;
+const CSR_CAPTURED_TRAP_CONTEXT_ADDR: usize = 0x31;
+const CSR_CAPTURER_TASK_CONTEXT_ADDR: usize = 0x32;
+const CSR_T0: usize = 0x33;
+const CSR_T1: usize = 0x34;
+
+#[unsafe(naked)]
+unsafe extern "C" fn _raw_trap_entry() -> ! {
+    naked_asm!(
+        // Page alignment is required for trap entry points
+        ".align 12",
+        "csrwr  $t0,  {CSR_T0}",
+        "csrwr  $t1,  {CSR_T1}",
+        "csrrd  $t0,  {CSR_CAPTURED_TRAP_CONTEXT_ADDR}",
+        "move   $t1,  $sp",
+        "bnez   $t0,  2f",
+        // We came here from normal execution
+        "li.d   $t0, -16",
+        "and    $t0,  $t0, $sp",
+        "addi.d $t0,  $t0, -{trap_context_size}",
+        "move   $sp,  $t0",
+        // t0: &mut TrapContext
+        "2:",
+        "st.d   $ra,  $t0, {ra}",
+        "st.d   $tp,  $t0, {tp}",
+        "st.d   $t1,  $t0, {sp}", // $sp is saved in $t1
+        "csrrd  $ra,  {CSR_T0}", // Put old $t0 into $ra
+        "csrrd  $tp,  {CSR_T1}", // Put old $t1 into $tp
+        "st.d   $a0,  $t0, {a0}",
+        "st.d   $a1,  $t0, {a1}",
+        "st.d   $a2,  $t0, {a2}",
+        "st.d   $a3,  $t0, {a3}",
+        "st.d   $a4,  $t0, {a4}",
+        "st.d   $a5,  $t0, {a5}",
+        "st.d   $a6,  $t0, {a6}",
+        "st.d   $a7,  $t0, {a7}",
+        "st.d   $ra,  $t0, {t0}", // $t0 is saved in $ra
+        "st.d   $tp,  $t0, {t1}", // $t1 is saved in $tp
+        "st.d   $t2,  $t0, {t2}",
+        "st.d   $t3,  $t0, {t3}",
+        "st.d   $t4,  $t0, {t4}",
+        "st.d   $t5,  $t0, {t5}",
+        "st.d   $t6,  $t0, {t6}",
+        "st.d   $t7,  $t0, {t7}",
+        "st.d   $t8,  $t0, {t8}",
+        "st.d   $r21, $t0, {u0}",
+        "st.d   $fp,  $t0, {fp}",
+        "csrrd  $tp,  {CSR_KERNEL_TP}",
+        "csrrd  $t1,  {CSR_ESTAT}",
+        "csrrd  $t2,  {CSR_PRMD}",
+        "csrrd  $ra,  {CSR_ERA}",
+        "csrrd  $a1,  {CSR_CAPTURER_TASK_CONTEXT_ADDR}",
+        "st.d   $s0,  $t0, {s0}",
+        "st.d   $s1,  $t0, {s1}",
+        "st.d   $s2,  $t0, {s2}",
+        "st.d   $s3,  $t0, {s3}",
+        "st.d   $s4,  $t0, {s4}",
+        "st.d   $s5,  $t0, {s5}",
+        "st.d   $s6,  $t0, {s6}",
+        "st.d   $s7,  $t0, {s7}",
+        "st.d   $s8,  $t0, {s8}",
+        "st.d   $t1,  $t0, {estat}",
+        "st.d   $t2,  $t0, {prmd}",
+        "st.d   $ra,  $t0, {era}",
+        "bnez   $a1,  {captured_trap_handler}",
+        "b      {default_trap_handler}",
+        CSR_KERNEL_TP = const CSR_KERNEL_TP,
+        CSR_CAPTURED_TRAP_CONTEXT_ADDR = const CSR_CAPTURED_TRAP_CONTEXT_ADDR,
+        CSR_CAPTURER_TASK_CONTEXT_ADDR = const CSR_CAPTURER_TASK_CONTEXT_ADDR,
+        CSR_T0 = const CSR_T0,
+        CSR_T1 = const CSR_T1,
+        CSR_ESTAT = const 0x5,
+        CSR_PRMD = const 0x1,
+        CSR_ERA = const 0x6,
+        trap_context_size = const size_of::<TrapContext>(),
+        ra = const Registers::OFFSET_RA,
+        tp = const Registers::OFFSET_TP,
+        sp = const Registers::OFFSET_SP,
+        a0 = const Registers::OFFSET_A0,
+        a1 = const Registers::OFFSET_A1,
+        a2 = const Registers::OFFSET_A2,
+        a3 = const Registers::OFFSET_A3,
+        a4 = const Registers::OFFSET_A4,
+        a5 = const Registers::OFFSET_A5,
+        a6 = const Registers::OFFSET_A6,
+        a7 = const Registers::OFFSET_A7,
+        t0 = const Registers::OFFSET_T0,
+        t1 = const Registers::OFFSET_T1,
+        t2 = const Registers::OFFSET_T2,
+        t3 = const Registers::OFFSET_T3,
+        t4 = const Registers::OFFSET_T4,
+        t5 = const Registers::OFFSET_T5,
+        t6 = const Registers::OFFSET_T6,
+        t7 = const Registers::OFFSET_T7,
+        t8 = const Registers::OFFSET_T8,
+        u0 = const Registers::OFFSET_U0,
+        fp = const Registers::OFFSET_FP,
+        s0 = const Registers::OFFSET_S0,
+        s1 = const Registers::OFFSET_S1,
+        s2 = const Registers::OFFSET_S2,
+        s3 = const Registers::OFFSET_S3,
+        s4 = const Registers::OFFSET_S4,
+        s5 = const Registers::OFFSET_S5,
+        s6 = const Registers::OFFSET_S6,
+        s7 = const Registers::OFFSET_S7,
+        s8 = const Registers::OFFSET_S8,
+        estat = const TrapContext::OFFSET_ESTAT,
+        prmd = const TrapContext::OFFSET_PRMD,
+        era = const TrapContext::OFFSET_ERA,
+        captured_trap_handler = sym captured_trap_handler,
+        default_trap_handler = sym default_trap_handler,
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn _raw_trap_return(ctx: &mut TrapContext) -> ! {
+    naked_asm!(
+        "ld.d  $ra,  $s8, {ra}",
+        "ld.d  $tp,  $s8, {tp}",
+        "ld.d  $sp,  $s8, {sp}",
+        "ld.d  $a0,  $s8, {a0}",
+        "ld.d  $a1,  $s8, {a1}",
+        "ld.d  $a2,  $s8, {a2}",
+        "ld.d  $a3,  $s8, {a3}",
+        "ld.d  $a4,  $s8, {a4}",
+        "ld.d  $a5,  $s8, {a5}",
+        "ld.d  $a6,  $s8, {a6}",
+        "ld.d  $a7,  $s8, {a7}",
+        "ld.d  $t0,  $s8, {t0}",
+        "ld.d  $t1,  $s8, {t1}",
+        "ld.d  $t2,  $s8, {t2}",
+        "ld.d  $t3,  $s8, {t3}",
+        "ld.d  $t4,  $s8, {t4}",
+        "ld.d  $t5,  $s8, {t5}",
+        "ld.d  $t6,  $s8, {t6}",
+        "ld.d  $t7,  $s8, {t7}",
+        "ld.d  $t8,  $s8, {t8}",
+        "ld.d  $r21, $s8, {u0}",
+        "ld.d  $fp,  $s8, {fp}",
+        "ld.d  $s6,  $s8, {prmd}",
+        "ld.d  $s7,  $s8, {era}",
+        "ld.d  $s0,  $s8, {s0}",
+        "ld.d  $s1,  $s8, {s1}",
+        "ld.d  $s2,  $s8, {s2}",
+        "ld.d  $s3,  $s8, {s3}",
+        "ld.d  $s4,  $s8, {s4}",
+        "ld.d  $s5,  $s8, {s5}",
+        "csrwr $s6,  {CSR_PRMD}",
+        "csrwr $s7,  {CSR_ERA}",
+        "ld.d  $s6,  $s8, {s6}",
+        "ld.d  $s7,  $s8, {s7}",
+        "ld.d  $s8,  $s8, {s8}",
+        "ertn",
+        CSR_PRMD = const 0x1,
+        CSR_ERA = const 0x6,
+        ra = const Registers::OFFSET_RA,
+        tp = const Registers::OFFSET_TP,
+        sp = const Registers::OFFSET_SP,
+        a0 = const Registers::OFFSET_A0,
+        a1 = const Registers::OFFSET_A1,
+        a2 = const Registers::OFFSET_A2,
+        a3 = const Registers::OFFSET_A3,
+        a4 = const Registers::OFFSET_A4,
+        a5 = const Registers::OFFSET_A5,
+        a6 = const Registers::OFFSET_A6,
+        a7 = const Registers::OFFSET_A7,
+        t0 = const Registers::OFFSET_T0,
+        t1 = const Registers::OFFSET_T1,
+        t2 = const Registers::OFFSET_T2,
+        t3 = const Registers::OFFSET_T3,
+        t4 = const Registers::OFFSET_T4,
+        t5 = const Registers::OFFSET_T5,
+        t6 = const Registers::OFFSET_T6,
+        t7 = const Registers::OFFSET_T7,
+        t8 = const Registers::OFFSET_T8,
+        u0 = const Registers::OFFSET_U0,
+        fp = const Registers::OFFSET_FP,
+        s0 = const Registers::OFFSET_S0,
+        s1 = const Registers::OFFSET_S1,
+        s2 = const Registers::OFFSET_S2,
+        s3 = const Registers::OFFSET_S3,
+        s4 = const Registers::OFFSET_S4,
+        s5 = const Registers::OFFSET_S5,
+        s6 = const Registers::OFFSET_S6,
+        s7 = const Registers::OFFSET_S7,
+        s8 = const Registers::OFFSET_S8,
+        prmd = const TrapContext::OFFSET_PRMD,
+        era = const TrapContext::OFFSET_ERA,
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn default_trap_handler() {
+    unsafe extern "C" {
+        fn _default_trap_handler(trap_context: &mut TrapContext);
+    }
+
+    #[cfg(debug_assertions)]
+    naked_asm!(
+        ".cfi_startproc",
+        ".cfi_signal_frame",
+        "move $s8, $t0",
+        "move $a0, $t0",
+        "",
+        ".cfi_register $ra, $s7",
+        "move $s7, $ra",
+        "",
+        "bl   {default_handler}",
+        "",
+        "b    {trap_return}",
+        "",
+        ".cfi_endproc",
+        default_handler = sym _default_trap_handler,
+        trap_return = sym _raw_trap_return,
+    );
+
+    #[cfg(not(debug_assertions))]
+    naked_asm!(
+        "move $s8, $t0",
+        "move $a0, $t0",
+        "",
+        "bl   {default_handler}",
+        "b    {trap_return}",
+        default_handler = sym _default_trap_handler,
+        trap_return = sym _raw_trap_return,
+    );
+}
+
+static DIRTY_TASK_CONTEXT: TaskContext = unsafe { core::mem::zeroed() };
+
+#[unsafe(naked)]
+unsafe extern "C" fn captured_trap_handler() {
+    naked_asm!(
+        "la.global $a0, {dirty_task_context}",
+        "b         {switch}",
+        dirty_task_context = sym DIRTY_TASK_CONTEXT,
+        switch = sym TaskContext::switch,
+    );
+}
+
+#[unsafe(naked)]
+unsafe extern "C" fn captured_trap_return(trap_context: usize) -> ! {
+    naked_asm!(
+        "move $s8, $sp",
+        "b    {raw_trap_return}",
+        raw_trap_return = sym _raw_trap_return,
+    );
+}
+
+impl TrapReturn for TrapContext {
+    type TaskContext = TaskContext;
+
+    unsafe fn trap_return(&mut self) {
+        let irq_states = disable_irqs_save();
+
+        let mut capturer_ctx = TaskContext::new();
+        let mut to_ctx = TaskContext::new();
+        to_ctx.set_program_counter(captured_trap_return as usize);
+        to_ctx.set_stack_pointer(&raw mut *self as usize);
+        to_ctx.set_interrupt_enabled(false);
+
+        unsafe {
+            let mut old_trap_ctx: usize;
+            let mut old_task_ctx: usize;
+
+            asm!(
+                "csrrd {old_trap_ctx}, {CSR_CAPTURED_TRAP_CONTEXT_ADDR}",
+                "csrrd {old_task_ctx}, {CSR_CAPTURER_TASK_CONTEXT_ADDR}",
+                "csrwr {captured_trap_context}, {CSR_CAPTURED_TRAP_CONTEXT_ADDR}",
+                "csrwr {capturer_task_context}, {CSR_CAPTURER_TASK_CONTEXT_ADDR}",
+                captured_trap_context = inout(reg) &raw mut *self => _,
+                capturer_task_context = inout(reg) &raw mut capturer_ctx => _,
+                old_trap_ctx = out(reg) old_trap_ctx,
+                old_task_ctx = out(reg) old_task_ctx,
+                CSR_CAPTURED_TRAP_CONTEXT_ADDR = const CSR_CAPTURED_TRAP_CONTEXT_ADDR,
+                CSR_CAPTURER_TASK_CONTEXT_ADDR = const CSR_CAPTURER_TASK_CONTEXT_ADDR,
+                options(nomem, nostack, preserves_flags),
+            );
+
+            TaskContext::switch(&mut capturer_ctx, &mut to_ctx);
+
+            asm!(
+                "csrwr {old_trap_ctx}, {CSR_CAPTURED_TRAP_CONTEXT_ADDR}",
+                "csrwr {old_task_ctx}, {CSR_CAPTURER_TASK_CONTEXT_ADDR}",
+                old_trap_ctx = inout(reg) old_trap_ctx,
+                old_task_ctx = inout(reg) old_task_ctx,
+                CSR_CAPTURED_TRAP_CONTEXT_ADDR = const CSR_CAPTURED_TRAP_CONTEXT_ADDR,
+                CSR_CAPTURER_TASK_CONTEXT_ADDR = const CSR_CAPTURER_TASK_CONTEXT_ADDR,
+                options(nomem, nostack, preserves_flags),
+            )
+        }
+
+        irq_states.restore();
+    }
+}
+
+fn setup_trap_handler(trap_entry_addr: usize) {
+    ecfg::set_vs(0);
+    eentry::set_eentry(trap_entry_addr);
+}
+
+pub fn setup_trap() {
+    setup_trap_handler(_raw_trap_entry as usize);
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct IrqState(bool);
+
+impl IrqState {
+    #[inline]
+    pub fn save() -> Self {
+        IrqState(crmd::read().ie())
+    }
+}
+
+impl IrqStateTrait for IrqState {
+    fn restore(self) {
+        let Self(state) = self;
+        crmd::set_ie(state)
+    }
+}
+
+#[inline]
+pub fn disable_irqs() {
+    crmd::set_ie(false);
+}
+
+#[inline]
+pub fn enable_irqs() {
+    crmd::set_ie(true);
+}
+
+#[inline]
+pub fn disable_irqs_save() -> IrqState {
+    let state = IrqState::save();
+    disable_irqs();
+
+    state
+}

+ 308 - 0
crates/eonix_hal/src/arch/loongarch64/trap/trap_context.rs

@@ -0,0 +1,308 @@
+use crate::{arch::trap::CSR_KERNEL_TP, processor::CPU};
+use core::{arch::asm, mem::offset_of};
+use eonix_hal_traits::{
+    fault::{Fault, PageFaultErrorCode},
+    trap::{RawTrapContext, TrapType},
+};
+use eonix_mm::address::VAddr;
+use loongArch64::register::{
+    badv,
+    estat::{Estat, Exception, Interrupt, Trap},
+    ticlr,
+};
+
+#[repr(C)]
+#[derive(Default, Clone, Copy)]
+pub struct Registers {
+    ra: u64,
+    tp: u64,
+    sp: u64,
+    a0: u64,
+    a1: u64,
+    a2: u64,
+    a3: u64,
+    a4: u64,
+    a5: u64,
+    a6: u64,
+    a7: u64,
+    t0: u64,
+    t1: u64,
+    t2: u64,
+    t3: u64,
+    t4: u64,
+    t5: u64,
+    t6: u64,
+    t7: u64,
+    t8: u64,
+    u0: u64,
+    fp: u64,
+    s0: u64,
+    s1: u64,
+    s2: u64,
+    s3: u64,
+    s4: u64,
+    s5: u64,
+    s6: u64,
+    s7: u64,
+    s8: u64,
+}
+
+/// Saved CPU context when a trap (interrupt or exception) occurs on RISC-V 64.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct TrapContext {
+    regs: Registers,
+    estat: Estat,
+    prmd: usize,
+    era: usize,
+}
+
+impl Registers {
+    pub const OFFSET_RA: usize = offset_of!(Registers, ra);
+    pub const OFFSET_TP: usize = offset_of!(Registers, tp);
+    pub const OFFSET_SP: usize = offset_of!(Registers, sp);
+    pub const OFFSET_A0: usize = offset_of!(Registers, a0);
+    pub const OFFSET_A1: usize = offset_of!(Registers, a1);
+    pub const OFFSET_A2: usize = offset_of!(Registers, a2);
+    pub const OFFSET_A3: usize = offset_of!(Registers, a3);
+    pub const OFFSET_A4: usize = offset_of!(Registers, a4);
+    pub const OFFSET_A5: usize = offset_of!(Registers, a5);
+    pub const OFFSET_A6: usize = offset_of!(Registers, a6);
+    pub const OFFSET_A7: usize = offset_of!(Registers, a7);
+    pub const OFFSET_T0: usize = offset_of!(Registers, t0);
+    pub const OFFSET_T1: usize = offset_of!(Registers, t1);
+    pub const OFFSET_T2: usize = offset_of!(Registers, t2);
+    pub const OFFSET_T3: usize = offset_of!(Registers, t3);
+    pub const OFFSET_T4: usize = offset_of!(Registers, t4);
+    pub const OFFSET_T5: usize = offset_of!(Registers, t5);
+    pub const OFFSET_T6: usize = offset_of!(Registers, t6);
+    pub const OFFSET_T7: usize = offset_of!(Registers, t7);
+    pub const OFFSET_T8: usize = offset_of!(Registers, t8);
+    pub const OFFSET_U0: usize = offset_of!(Registers, u0);
+    pub const OFFSET_FP: usize = offset_of!(Registers, fp);
+    pub const OFFSET_S0: usize = offset_of!(Registers, s0);
+    pub const OFFSET_S1: usize = offset_of!(Registers, s1);
+    pub const OFFSET_S2: usize = offset_of!(Registers, s2);
+    pub const OFFSET_S3: usize = offset_of!(Registers, s3);
+    pub const OFFSET_S4: usize = offset_of!(Registers, s4);
+    pub const OFFSET_S5: usize = offset_of!(Registers, s5);
+    pub const OFFSET_S6: usize = offset_of!(Registers, s6);
+    pub const OFFSET_S7: usize = offset_of!(Registers, s7);
+    pub const OFFSET_S8: usize = offset_of!(Registers, s8);
+}
+
+type FIrq = fn(handler: fn(irqno: usize));
+type FTimer = fn(handler: fn());
+
+impl TrapContext {
+    pub const OFFSET_ESTAT: usize = offset_of!(TrapContext, estat);
+    pub const OFFSET_PRMD: usize = offset_of!(TrapContext, prmd);
+    pub const OFFSET_ERA: usize = offset_of!(TrapContext, era);
+
+    fn syscall_no(&self) -> usize {
+        self.regs.a7 as usize
+    }
+
+    fn syscall_args(&self) -> [usize; 6] {
+        [
+            self.regs.a0 as usize,
+            self.regs.a1 as usize,
+            self.regs.a2 as usize,
+            self.regs.a3 as usize,
+            self.regs.a4 as usize,
+            self.regs.a5 as usize,
+        ]
+    }
+
+    fn gen_page_fault(&self, mut err_code: PageFaultErrorCode) -> TrapType<FIrq, FTimer> {
+        #[inline(always)]
+        fn get_page_fault_address() -> VAddr {
+            VAddr::from(badv::read().vaddr())
+        }
+
+        err_code.set(PageFaultErrorCode::UserAccess, self.is_user_mode());
+
+        TrapType::Fault(Fault::PageFault {
+            error_code: err_code,
+            address: get_page_fault_address(),
+        })
+    }
+}
+
+impl RawTrapContext for TrapContext {
+    type FIrq = FIrq;
+    type FTimer = FTimer;
+
+    fn new() -> Self {
+        Self {
+            regs: Registers::default(),
+            estat: Estat::from(0),
+            prmd: 0,
+            era: 0,
+        }
+    }
+
+    fn trap_type(&self) -> TrapType<Self::FIrq, Self::FTimer> {
+        match self.estat.cause() {
+            Trap::Interrupt(Interrupt::Timer) => TrapType::Timer {
+                callback: |handler| {
+                    ticlr::clear_timer_interrupt();
+                    handler();
+                },
+            },
+            Trap::Interrupt(interrupt) => match interrupt as usize {
+                2..=7 => TrapType::Irq {
+                    callback: |handler| {
+                        todo!("handle IRQs");
+                        // let mut cpu = CPU::local();
+                        // match cpu.as_mut().interrupt.plic.claim_interrupt() {
+                        //     None => {}
+                        //     Some(irqno) => {
+                        //         cpu.interrupt.plic.complete_interrupt(irqno);
+                        //         handler(irqno);
+                        //     }
+                        // }
+                    },
+                },
+                interrupt => TrapType::Fault(Fault::Unknown(interrupt)),
+            },
+            Trap::Exception(
+                Exception::InstructionPrivilegeIllegal
+                | Exception::FetchInstructionAddressError
+                | Exception::AddressNotAligned
+                | Exception::MemoryAccessAddressError
+                | Exception::PagePrivilegeIllegal,
+            ) => TrapType::Fault(Fault::BadAccess),
+            Trap::Exception(Exception::Breakpoint) => TrapType::Breakpoint,
+            Trap::Exception(Exception::InstructionNotExist) => TrapType::Fault(Fault::InvalidOp),
+            Trap::Exception(Exception::Syscall) => TrapType::Syscall {
+                no: self.syscall_no(),
+                args: self.syscall_args(),
+            },
+            Trap::Exception(Exception::LoadPageFault | Exception::PageNonReadableFault) => {
+                self.gen_page_fault(PageFaultErrorCode::Read)
+            }
+            Trap::Exception(Exception::StorePageFault | Exception::PageModifyFault) => {
+                self.gen_page_fault(PageFaultErrorCode::Write)
+            }
+            Trap::Exception(Exception::FetchPageFault | Exception::PageNonExecutableFault) => {
+                self.gen_page_fault(PageFaultErrorCode::InstructionFetch)
+            }
+            Trap::Exception(exception) => TrapType::Fault(Fault::Unknown(exception as usize)),
+            Trap::MachineError(_) | Trap::Unknown => todo!(),
+        }
+    }
+
+    fn get_program_counter(&self) -> usize {
+        self.era
+    }
+
+    fn get_stack_pointer(&self) -> usize {
+        self.regs.sp as usize
+    }
+
+    fn set_program_counter(&mut self, pc: usize) {
+        self.era = pc;
+    }
+
+    fn set_stack_pointer(&mut self, sp: usize) {
+        self.regs.sp = sp as u64;
+    }
+
+    fn is_interrupt_enabled(&self) -> bool {
+        self.prmd & (1 << 2) != 0
+    }
+
+    fn set_interrupt_enabled(&mut self, enabled: bool) {
+        match enabled {
+            true => self.prmd |= 1 << 2,
+            false => self.prmd &= !(1 << 2),
+        }
+    }
+
+    fn is_user_mode(&self) -> bool {
+        self.prmd & 0x3 != 0
+    }
+
+    fn set_user_mode(&mut self, user: bool) {
+        match user {
+            true => self.prmd |= 0x3,
+            false => {
+                unsafe {
+                    asm!(
+                        "csrrd {tp}, {CSR_KERNEL_TP}",
+                        tp = out(reg) self.regs.tp,
+                        CSR_KERNEL_TP = const CSR_KERNEL_TP,
+                        options(nomem, nostack, preserves_flags),
+                    )
+                }
+                self.prmd &= !0x3;
+            }
+        }
+    }
+
+    fn set_user_return_value(&mut self, retval: usize) {
+        self.regs.a0 = retval as u64;
+    }
+
+    fn set_user_call_frame<E>(
+        &mut self,
+        pc: usize,
+        sp: Option<usize>,
+        ra: Option<usize>,
+        args: &[usize],
+        _write_memory: impl Fn(VAddr, &[u8]) -> Result<(), E>,
+    ) -> Result<(), E> {
+        self.set_program_counter(pc);
+
+        if let Some(sp) = sp {
+            self.set_stack_pointer(sp);
+        }
+
+        if let Some(ra) = ra {
+            self.regs.ra = ra as u64;
+        }
+
+        let arg_regs = [
+            &mut self.regs.a0,
+            &mut self.regs.a1,
+            &mut self.regs.a2,
+            &mut self.regs.a3,
+            &mut self.regs.a4,
+            &mut self.regs.a5,
+        ];
+
+        for (&arg, reg) in args.iter().zip(arg_regs.into_iter()) {
+            *reg = arg as u64;
+        }
+
+        Ok(())
+    }
+}
+
+impl TrapContext {
+    fn get_page_fault_error_code(&self, exception: Exception) -> PageFaultErrorCode {
+        let mut error_code = PageFaultErrorCode::empty();
+
+        match exception {
+            Exception::FetchPageFault => {
+                error_code |= PageFaultErrorCode::InstructionFetch;
+            }
+            Exception::LoadPageFault => {
+                error_code |= PageFaultErrorCode::Read;
+            }
+            Exception::StorePageFault => {
+                error_code |= PageFaultErrorCode::Write;
+            }
+            _ => {
+                unreachable!();
+            }
+        }
+
+        if self.is_user_mode() {
+            error_code |= PageFaultErrorCode::UserAccess;
+        }
+
+        error_code
+    }
+}

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

@@ -0,0 +1,18 @@
+#![allow(warnings)]
+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 if #[cfg(target_arch = "loongarch64")] {
+        pub mod loongarch64;
+        pub use loongarch64::*;
+    } else {
+        compile_error!("Unsupported architecture");
+    }
+}

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

@@ -0,0 +1,393 @@
+use super::{
+    config::{self, mm::*},
+    console::write_str,
+    cpu::{CPUID, CPU_COUNT},
+    time::set_next_timer,
+};
+use crate::{
+    arch::{
+        cpu::CPU,
+        fdt::{init_dtb_and_fdt, FdtExt, FDT},
+        mm::{ArchPhysAccess, FreeRam, PageAttribute64, GLOBAL_PAGE_TABLE},
+    },
+    bootstrap::BootStrapData,
+    mm::{ArchMemory, ArchPagingMode, BasicPageAlloc, BasicPageAllocRef, ScopedAllocator},
+};
+use core::{
+    alloc::Allocator,
+    arch::asm,
+    cell::RefCell,
+    sync::atomic::{AtomicBool, AtomicUsize},
+};
+use core::{
+    arch::{global_asm, naked_asm},
+    hint::spin_loop,
+    sync::atomic::{AtomicPtr, Ordering},
+};
+use eonix_hal_traits::mm::Memory;
+use eonix_mm::{
+    address::{Addr as _, PAddr, PRange, PhysAccess, VAddr, VRange},
+    page_table::{PageAttribute, PagingMode, PTE as _},
+    paging::{Page, PageAccess, PageAlloc, PAGE_SIZE, PFN},
+};
+use eonix_percpu::PercpuArea;
+use fdt::Fdt;
+use riscv::{asm::sfence_vma_all, register::satp};
+use sbi::{hsm::hart_start, legacy::console_putchar, PhysicalAddress};
+
+#[unsafe(link_section = ".bootstrap.stack")]
+static BOOT_STACK: [u8; 4096 * 16] = [0; 4096 * 16];
+
+static BOOT_STACK_START: &'static [u8; 4096 * 16] = &BOOT_STACK;
+
+#[unsafe(link_section = ".bootstrap.stack")]
+static TEMP_AP_STACK: [u8; 256] = [0; 256];
+
+static TEMP_AP_STACK_START: &'static [u8; 256] = &TEMP_AP_STACK;
+
+#[repr(C, align(4096))]
+struct PageTable([u64; PTES_PER_PAGE]);
+
+/// map 0x8000 0000 to itself and 0xffff ffff 8000 0000
+#[unsafe(link_section = ".bootstrap.page_table.1")]
+static BOOT_PAGE_TABLE: PageTable = {
+    let mut arr: [u64; PTES_PER_PAGE] = [0; PTES_PER_PAGE];
+    arr[0] = 0 | 0x2f;
+    arr[510] = 0 | 0x2f;
+    arr[511] = (0x80202 << 10) | 0x21;
+
+    PageTable(arr)
+};
+
+#[unsafe(link_section = ".bootstrap.page_table.2")]
+#[used]
+static PT1: PageTable = {
+    let mut arr: [u64; PTES_PER_PAGE] = [0; PTES_PER_PAGE];
+    arr[510] = (0x80000 << 10) | 0x2f;
+
+    PageTable(arr)
+};
+
+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);
+
+/// bootstrap in rust
+#[unsafe(naked)]
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".bootstrap.entry")]
+unsafe extern "C" fn _start(hart_id: usize, dtb_addr: usize) -> ! {
+    naked_asm!(
+        "
+            ld    sp, 2f
+            li    t0, 0x10000
+            add   sp, sp, t0
+            ld    t0, 3f
+            srli  t0, t0, 12
+            li    t1, 9 << 60
+            or    t0, t0, t1
+            csrw  satp, t0
+            sfence.vma
+            ld    t0, 4f
+            jalr  t0                      // call riscv64_start
+
+            .pushsection .bootstrap.data, \"aw\", @progbits
+            2:
+            .8byte {boot_stack}
+            3:
+            .8byte {page_table}
+            4:
+            .8byte {riscv64_start}
+            .popsection
+        ",
+        boot_stack = sym BOOT_STACK,
+        page_table = sym BOOT_PAGE_TABLE,
+        riscv64_start = sym riscv64_start,
+    )
+}
+
+pub unsafe extern "C" fn riscv64_start(hart_id: usize, dtb_addr: PAddr) -> ! {
+    let fdt = Fdt::from_ptr(ArchPhysAccess::as_ptr(dtb_addr).as_ptr())
+        .expect("Failed to parse DTB from static memory.");
+
+    let real_allocator = RefCell::new(BasicPageAlloc::new());
+    let alloc = BasicPageAllocRef::new(&real_allocator);
+
+    for range in fdt.present_ram().free_ram() {
+        real_allocator.borrow_mut().add_range(range);
+    }
+
+    setup_kernel_page_table(&alloc);
+    unsafe {
+        init_dtb_and_fdt(dtb_addr);
+    }
+
+    setup_cpu(&alloc, hart_id);
+
+    ScopedAllocator::new(&mut [0; 1024])
+        .with_alloc(|mem_alloc| bootstrap_smp(mem_alloc, &real_allocator));
+
+    unsafe extern "Rust" {
+        fn _eonix_hal_main(_: BootStrapData) -> !;
+    }
+
+    let start = unsafe {
+        ((&BOOT_STACK_START) as *const &'static [u8; 4096 * 16]).read_volatile() as *const _
+            as usize
+    };
+    let bootstrap_data = BootStrapData {
+        early_stack: PRange::new(PAddr::from(start), PAddr::from(start + 4096 * 16)),
+        allocator: Some(real_allocator),
+    };
+
+    // set current hart's mtimecmp register
+    set_next_timer();
+
+    unsafe {
+        _eonix_hal_main(bootstrap_data);
+    }
+}
+
+unsafe extern "C" {
+    fn BSS_LENGTH();
+}
+
+/// TODO:
+/// 对kernel image添加更细的控制,或者不加也行
+fn setup_kernel_page_table(alloc: impl PageAlloc) {
+    let global_page_table = &GLOBAL_PAGE_TABLE;
+
+    let attr = PageAttribute::WRITE
+        | PageAttribute::READ
+        | PageAttribute::EXECUTE
+        | PageAttribute::GLOBAL
+        | PageAttribute::PRESENT;
+
+    const KERNEL_BSS_START: VAddr = VAddr::from(0xffffffff40000000);
+
+    // Map kernel BSS
+    for pte in global_page_table.iter_kernel_in(
+        VRange::from(KERNEL_BSS_START).grow(BSS_LENGTH as usize),
+        ArchPagingMode::LEVELS,
+        &alloc,
+    ) {
+        let page = Page::alloc_in(&alloc);
+
+        let attr = {
+            let mut attr = attr.clone();
+            attr.remove(PageAttribute::EXECUTE);
+            attr
+        };
+        pte.set(page.into_raw(), attr.into());
+    }
+
+    sfence_vma_all();
+
+    unsafe {
+        core::ptr::write_bytes(KERNEL_BSS_START.addr() as *mut (), 0, BSS_LENGTH as usize);
+    }
+
+    unsafe {
+        satp::set(
+            satp::Mode::Sv48,
+            0,
+            usize::from(PFN::from(global_page_table.addr())),
+        );
+    }
+    sfence_vma_all();
+}
+
+/// set up tp register to percpu
+fn setup_cpu(alloc: impl PageAlloc, hart_id: usize) {
+    CPU_COUNT.fetch_add(1, Ordering::Relaxed);
+
+    let mut percpu_area = PercpuArea::new(|layout| {
+        let page_count = layout.size().div_ceil(PAGE_SIZE);
+        let page = Page::alloc_at_least_in(page_count, alloc);
+
+        let ptr = ArchPhysAccess::get_ptr_for_page(&page).cast();
+        page.into_raw();
+
+        ptr
+    });
+
+    // set tp(x4) register
+    percpu_area.setup(|pointer| {
+        let percpu_base_addr = pointer.addr().get();
+        unsafe {
+            asm!(
+                "mv tp, {0}",
+                in(reg) percpu_base_addr,
+                options(nostack, preserves_flags)
+            );
+        }
+    });
+
+    CPUID.set(hart_id);
+
+    let mut cpu = CPU::local();
+    unsafe {
+        cpu.as_mut().init();
+    }
+
+    percpu_area.register(cpu.cpuid());
+}
+
+fn get_ap_start_addr() -> usize {
+    unsafe extern "C" {
+        fn _ap_start();
+    }
+    static AP_START_VALUE: &'static unsafe extern "C" fn() =
+        &(_ap_start as unsafe extern "C" fn());
+    unsafe { (AP_START_VALUE as *const _ as *const usize).read_volatile() }
+}
+
+fn bootstrap_smp(alloc: impl Allocator, page_alloc: &RefCell<BasicPageAlloc>) {
+    let local_hart_id = CPU::local().cpuid();
+    let mut ap_count = 0;
+
+    for hart_id in FDT.harts().filter(|&id| id != local_hart_id) {
+        let stack_range = {
+            let page_alloc = BasicPageAllocRef::new(&page_alloc);
+            let ap_stack = Page::alloc_order_in(4, page_alloc);
+            let stack_range = ap_stack.range();
+            ap_stack.into_raw();
+            stack_range
+        };
+
+        let old = BSP_PAGE_ALLOC.swap((&raw const *page_alloc) as *mut _, Ordering::Release);
+        assert!(old.is_null());
+
+        while AP_STACK
+            .compare_exchange_weak(
+                0,
+                stack_range.end().addr(),
+                Ordering::Release,
+                Ordering::Relaxed,
+            )
+            .is_err()
+        {
+            spin_loop();
+        }
+
+        unsafe {
+            hart_start(hart_id, PhysicalAddress::new(get_ap_start_addr()), 0);
+        }
+
+        while AP_COUNT.load(Ordering::Acquire) == ap_count {
+            spin_loop();
+        }
+
+        let old = BSP_PAGE_ALLOC.swap(core::ptr::null_mut(), Ordering::Acquire);
+        assert_eq!(old as *const _, &raw const *page_alloc);
+        ap_count += 1;
+    }
+}
+
+#[unsafe(naked)]
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".bootstrap.apentry")]
+unsafe extern "C" fn _ap_start(hart_id: usize) -> ! {
+    naked_asm!(
+        "
+            la    sp, 1f        // set temp stack
+            mv    s0, a0        // save hart id
+
+            ld    t0, 2f
+            srli  t0, t0, 12
+            li    t1, 9 << 60
+            or    t0, t0, t1
+            csrw  satp, t0
+            sfence.vma
+
+            ld    t0, 3f
+            jalr  t0
+            mv    sp, a0
+
+            mv    a0, s0
+            ld    t0, 4f
+            jalr  t0
+
+            .pushsection .bootstrap.data, \"aw\", @progbits
+            1: .8byte {temp_stack}
+            2: .8byte {page_table}
+            3: .8byte {get_ap_stack}
+            4: .8byte {ap_entry}
+            .popsection
+        ",
+        temp_stack = sym TEMP_AP_STACK_START,
+        page_table = sym BOOT_PAGE_TABLE,
+        get_ap_stack = sym get_ap_stack,
+        ap_entry = sym ap_entry,
+    )
+}
+
+fn get_ap_stack() -> usize {
+    while AP_SEM
+        .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
+        .is_err()
+    {
+        core::hint::spin_loop();
+    }
+
+    let stack_addr = loop {
+        let addr = AP_STACK.swap(0, Ordering::AcqRel);
+        if addr != 0 {
+            break addr;
+        }
+        core::hint::spin_loop();
+    };
+
+    AP_SEM.store(false, Ordering::Release);
+
+    stack_addr
+}
+
+fn ap_entry(hart_id: usize, 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), hart_id);
+
+        // 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) -> !;
+    }
+
+    // set current hart's mtimecmp register
+    set_next_timer();
+
+    unsafe {
+        _eonix_hal_ap_main(stack_range);
+    }
+}
+
+pub fn early_console_write(s: &str) {
+    write_str(s);
+}
+
+pub fn early_console_putchar(ch: u8) {
+    console_putchar(ch);
+}
+
+pub fn shutdown() -> ! {
+    sbi::legacy::shutdown();
+}

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

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

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

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

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

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

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

@@ -0,0 +1,93 @@
+use super::{
+    interrupt::InterruptControl,
+    trap::{setup_trap, TrapContext},
+};
+use crate::arch::fdt::{FdtExt, FDT};
+use core::{
+    arch::asm, cell::UnsafeCell, mem::MaybeUninit, pin::Pin, ptr::NonNull,
+    sync::atomic::AtomicUsize,
+};
+use eonix_hal_traits::trap::RawTrapContext;
+use eonix_preempt::PreemptGuard;
+use eonix_sync_base::LazyLock;
+use riscv::register::{
+    medeleg::{self, Medeleg},
+    mhartid, sscratch, sstatus,
+};
+use sbi::PhysicalAddress;
+
+pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(0);
+
+#[eonix_percpu::define_percpu]
+pub static CPUID: usize = 0;
+
+#[eonix_percpu::define_percpu]
+static DEFAULT_TRAP_CONTEXT: MaybeUninit<TrapContext> = MaybeUninit::uninit();
+
+#[eonix_percpu::define_percpu]
+static LOCAL_CPU: LazyLock<CPU> = LazyLock::new(|| CPU::new(CPUID.get()));
+
+#[derive(Debug, Clone)]
+pub enum UserTLS {
+    Base(u64),
+}
+
+/// RISC-V Hart
+pub struct CPU {
+    pub(crate) interrupt: InterruptControl,
+}
+
+impl UserTLS {
+    pub fn new(base: u64) -> Self {
+        Self::Base(base)
+    }
+}
+
+impl CPU {
+    fn new(cpuid: usize) -> Self {
+        Self {
+            interrupt: InterruptControl::new(cpuid),
+        }
+    }
+
+    /// Load CPU specific configurations for the current Hart.
+    ///
+    /// # Safety
+    /// This function performs low-level hardware initialization and should
+    /// only be called once per Hart during its boot sequence.
+    pub unsafe fn init(mut self: Pin<&mut Self>) {
+        let me = self.as_mut().get_unchecked_mut();
+        setup_trap();
+
+        let interrupt = self.map_unchecked_mut(|me| &mut me.interrupt);
+        interrupt.init();
+
+        sstatus::set_sum();
+        sscratch::write(DEFAULT_TRAP_CONTEXT.as_ptr() as usize);
+    }
+
+    pub unsafe fn load_interrupt_stack(self: Pin<&mut Self>, sp: u64) {}
+
+    pub fn set_tls32(self: Pin<&mut Self>, _user_tls: &UserTLS) {
+        // nothing
+    }
+
+    pub fn local() -> PreemptGuard<Pin<&'static mut Self>> {
+        unsafe {
+            // SAFETY: We pass the reference into a `PreemptGuard`, which ensures
+            //         that preemption is disabled.
+            PreemptGuard::new(Pin::new_unchecked(LOCAL_CPU.as_mut().get_mut()))
+        }
+    }
+
+    pub fn cpuid(&self) -> usize {
+        CPUID.get()
+    }
+}
+
+#[inline(always)]
+pub fn halt() {
+    unsafe {
+        asm!("wfi", options(nomem, nostack, preserves_flags));
+    }
+}

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

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

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

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

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

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

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

@@ -0,0 +1,152 @@
+use super::{config::platform::virt::*, fdt::FDT, fence::memory_barrier, mm::ArchPhysAccess};
+use crate::arch::time;
+use core::{pin::Pin, ptr::NonNull};
+use eonix_mm::address::{PAddr, PhysAccess};
+use eonix_sync_base::LazyLock;
+use riscv::register::sie;
+use sbi::SbiError;
+
+const PRIORITY_OFFSET: usize = 0x0;
+const PENDING_OFFSET: usize = 0x1000;
+
+const ENABLE_OFFSET: usize = 0x2000;
+const THRESHOLD_OFFSET: usize = 0x200000;
+const CLAIM_COMPLETE_OFFSET: usize = 0x200004;
+
+const ENABLE_STRIDE: usize = 0x80;
+const CONTEXT_STRIDE: usize = 0x1000;
+
+static PLIC_BASE: LazyLock<PAddr> = LazyLock::new(|| {
+    let plic = FDT
+        .find_compatible(&["riscv,plic0", "riscv,plic1"])
+        .expect("Failed to find PLIC in FDT");
+
+    let reg = plic
+        .reg()
+        .expect("PLIC node has no reg property")
+        .next()
+        .expect("PLIC reg property is empty");
+
+    PAddr::from(reg.starting_address as usize)
+});
+
+pub struct PLIC {
+    enable: NonNull<u32>,
+    threshold: NonNull<u32>,
+    claim_complete: NonNull<u32>,
+}
+
+pub struct InterruptControl {
+    pub plic: PLIC,
+}
+
+impl PLIC {
+    fn new(cpuid: usize) -> Self {
+        let base = *PLIC_BASE.get();
+
+        let enable = PAddr::from(base + (cpuid * 2 + 1) * ENABLE_STRIDE + ENABLE_OFFSET);
+        let threshold = PAddr::from(base + (cpuid * 2 + 1) * CONTEXT_STRIDE + THRESHOLD_OFFSET);
+        let claim_complete =
+            PAddr::from(base + (cpuid * 2 + 1) * CONTEXT_STRIDE + CLAIM_COMPLETE_OFFSET);
+
+        unsafe {
+            // SAFETY: The PLIC registers are memory-mapped and placed at specific addresses.
+            //         We are pretty sure that the addresses are valid.
+            Self {
+                enable: ArchPhysAccess::as_ptr(enable),
+                threshold: ArchPhysAccess::as_ptr(threshold),
+                claim_complete: ArchPhysAccess::as_ptr(claim_complete),
+            }
+        }
+    }
+
+    pub fn set_threshold(&self, threshold: u32) {
+        unsafe {
+            self.threshold.write_volatile(threshold);
+        }
+    }
+
+    pub fn set_priority(&self, interrupt: usize, priority: u32) {
+        let priority_ptr = unsafe {
+            // SAFETY: The PLIC priority register is memory-mapped and placed at a specific address.
+            //         We are pretty sure that the address is valid.
+            ArchPhysAccess::as_ptr(
+                *PLIC_BASE.get() + PRIORITY_OFFSET + interrupt * size_of::<u32>(),
+            )
+        };
+
+        memory_barrier();
+
+        unsafe {
+            priority_ptr.write_volatile(priority);
+        }
+
+        memory_barrier();
+    }
+
+    pub fn claim_interrupt(&self) -> Option<usize> {
+        match unsafe { self.claim_complete.read_volatile() } {
+            0 => None,
+            interrupt => Some(interrupt as usize),
+        }
+    }
+
+    pub fn complete_interrupt(&self, interrupt: usize) {
+        unsafe {
+            self.claim_complete.write_volatile(interrupt as u32);
+        }
+    }
+
+    pub fn enable_interrupt(&self, interrupt: usize) {
+        debug_assert!(interrupt < 1024, "Interrupt number out of range");
+
+        let enable_ptr = unsafe {
+            // SAFETY: Interrupt number is guaranteed to be less than 1024,
+            //         so we won't overflow the enable register array.
+            self.enable.add(interrupt / 32)
+        };
+
+        let bit = 1 << (interrupt % 32);
+        unsafe {
+            enable_ptr.write_volatile(enable_ptr.read_volatile() | bit);
+        }
+    }
+
+    pub fn disable_interrupt(&self, interrupt: usize) {
+        let enable_ptr = unsafe {
+            // SAFETY: Interrupt number is guaranteed to be less than 1024,
+            //         so we won't overflow the enable register array.
+            self.enable.add(interrupt / 32)
+        };
+
+        let bit = 1 << (interrupt % 32);
+        unsafe {
+            enable_ptr.write_volatile(enable_ptr.read_volatile() & !bit);
+        }
+    }
+}
+
+impl InterruptControl {
+    /// # Safety
+    /// should be called only once.
+    pub(crate) fn new(cpuid: usize) -> Self {
+        Self {
+            plic: PLIC::new(cpuid),
+        }
+    }
+
+    pub fn init(self: Pin<&mut Self>) {
+        self.plic.set_threshold(0);
+
+        // TODO: We should enable interrupts only when we register a handler.
+        for i in 0..32 {
+            self.plic.set_priority(i, 1);
+            self.plic.enable_interrupt(i);
+        }
+
+        unsafe {
+            sie::set_stimer();
+            sie::set_sext();
+        }
+    }
+}

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

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

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

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

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

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

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

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

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

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

+ 177 - 0
crates/eonix_hal/src/arch/riscv64/trap/captured.rs

@@ -0,0 +1,177 @@
+use crate::{arch::trap::Registers, context::TaskContext, trap::TrapContext};
+use core::{arch::naked_asm, mem::MaybeUninit};
+use eonix_hal_traits::context::RawTaskContext;
+
+static mut DIRTY_TASK_CONTEXT: MaybeUninit<TaskContext> = MaybeUninit::uninit();
+
+// If captured trap context is present, we use it directly.
+// We need to restore the kernel tp from that TrapContext but sp is
+// fine since we will use TaskContext::switch.
+#[unsafe(naked)]
+pub(super) unsafe extern "C" fn _captured_trap_entry() -> ! {
+    naked_asm!(
+        "csrrw t0, sscratch, t0",
+        "sd    tp, {tp}(t0)",
+        "ld    tp, {ra}(t0)", // Load kernel tp from trap_ctx.ra
+        "sd    ra, {ra}(t0)",
+        "ld    ra, {sp}(t0)", // Load capturer task context from trap_ctx.sp
+        "sd    sp, {sp}(t0)",
+        "sd    gp, {gp}(t0)",
+        "sd    a0, {a0}(t0)",
+        "sd    a1, {a1}(t0)",
+        "sd    a2, {a2}(t0)",
+        "sd    a3, {a3}(t0)",
+        "sd    a4, {a4}(t0)",
+        "sd    t1, {t1}(t0)",
+        "sd    a5, {a5}(t0)",
+        "sd    a6, {a6}(t0)",
+        "sd    a7, {a7}(t0)",
+        "sd    t3, {t3}(t0)",
+        "sd    t4, {t4}(t0)",
+        "sd    t5, {t5}(t0)",
+        "sd    t2, {t2}(t0)",
+        "sd    t6, {t6}(t0)",
+        "sd    s0, {s0}(t0)",
+        "sd    s1, {s1}(t0)",
+        "sd    s2, {s2}(t0)",
+        "sd    s3, {s3}(t0)",
+        "sd    s4, {s4}(t0)",
+        "sd    s5, {s5}(t0)",
+        "sd    s6, {s6}(t0)",
+        "sd    s7, {s7}(t0)",
+        "sd    s8, {s8}(t0)",
+        "sd    s9, {s9}(t0)",
+        "sd    s10, {s10}(t0)",
+        "sd    s11, {s11}(t0)",
+        "csrr  t2, sstatus",
+        "csrr  t3, sepc",
+        "csrr  t4, scause",
+        "csrr  t5, stval",
+        "csrrw t6, sscratch, t0",
+        "sd    t6, {t0}(t0)",
+        "sd    t2, {sstatus}(t0)",
+        "sd    t3, {sepc}(t0)",
+        "sd    t4, {scause}(t0)",
+        "sd    t5, {stval}(t0)",
+        "la    a0, {dirty_task_context}",
+        "mv    a1, ra",
+        "j     {task_context_switch}",
+        ra = const Registers::OFFSET_RA,
+        sp = const Registers::OFFSET_SP,
+        gp = const Registers::OFFSET_GP,
+        tp = const Registers::OFFSET_TP,
+        t1 = const Registers::OFFSET_T1,
+        t2 = const Registers::OFFSET_T2,
+        t0 = const Registers::OFFSET_T0,
+        a0 = const Registers::OFFSET_A0,
+        a1 = const Registers::OFFSET_A1,
+        a2 = const Registers::OFFSET_A2,
+        a3 = const Registers::OFFSET_A3,
+        a4 = const Registers::OFFSET_A4,
+        a5 = const Registers::OFFSET_A5,
+        a6 = const Registers::OFFSET_A6,
+        a7 = const Registers::OFFSET_A7,
+        t3 = const Registers::OFFSET_T3,
+        t4 = const Registers::OFFSET_T4,
+        t5 = const Registers::OFFSET_T5,
+        t6 = const Registers::OFFSET_T6,
+        s0 = const Registers::OFFSET_S0,
+        s1 = const Registers::OFFSET_S1,
+        s2 = const Registers::OFFSET_S2,
+        s3 = const Registers::OFFSET_S3,
+        s4 = const Registers::OFFSET_S4,
+        s5 = const Registers::OFFSET_S5,
+        s6 = const Registers::OFFSET_S6,
+        s7 = const Registers::OFFSET_S7,
+        s8 = const Registers::OFFSET_S8,
+        s9 = const Registers::OFFSET_S9,
+        s10 = const Registers::OFFSET_S10,
+        s11 = const Registers::OFFSET_S11,
+        sstatus = const TrapContext::OFFSET_SSTATUS,
+        sepc = const TrapContext::OFFSET_SEPC,
+        scause = const TrapContext::OFFSET_SCAUSE,
+        stval = const TrapContext::OFFSET_STVAL,
+        dirty_task_context = sym DIRTY_TASK_CONTEXT,
+        task_context_switch = sym TaskContext::switch,
+    );
+}
+
+#[unsafe(naked)]
+pub(super) unsafe extern "C" fn _captured_trap_return(ctx: &mut TrapContext) -> ! {
+    naked_asm!(
+        "csrr   t0,  sscratch",
+        "ld     t1,  {sstatus}(t0)",
+        "ld     t2,  {sepc}(t0)",
+        "csrw   sstatus, t1",
+        "csrw   sepc, t2",
+        "mv     t4,  tp",
+        "mv     t5,  sp",
+        "ld     tp,  {tp}(t0)",
+        "ld     ra,  {ra}(t0)",
+        "ld     sp,  {sp}(t0)",
+        "sd     t4,  {ra}(t0)", // Store kernel tp to trap_ctx.ra
+        "sd     t5,  {sp}(t0)", // Store capturer task context to trap_ctx.sp
+        "ld     gp,  {gp}(t0)",
+        "ld     a0,  {a0}(t0)",
+        "ld     a1,  {a1}(t0)",
+        "ld     a2,  {a2}(t0)",
+        "ld     a3,  {a3}(t0)",
+        "ld     a4,  {a4}(t0)",
+        "ld     t1,  {t1}(t0)",
+        "ld     a5,  {a5}(t0)",
+        "ld     a6,  {a6}(t0)",
+        "ld     a7,  {a7}(t0)",
+        "ld     t3,  {t3}(t0)",
+        "ld     t4,  {t4}(t0)",
+        "ld     t5,  {t5}(t0)",
+        "ld     t2,  {t2}(t0)",
+        "ld     t6,  {t6}(t0)",
+        "ld     s0,  {s0}(t0)",
+        "ld     s1,  {s1}(t0)",
+        "ld     s2,  {s2}(t0)",
+        "ld     s3,  {s3}(t0)",
+        "ld     s4,  {s4}(t0)",
+        "ld     s5,  {s5}(t0)",
+        "ld     s6,  {s6}(t0)",
+        "ld     s7,  {s7}(t0)",
+        "ld     s8,  {s8}(t0)",
+        "ld     s9,  {s9}(t0)",
+        "ld     s10, {s10}(t0)",
+        "ld     s11, {s11}(t0)",
+        "ld     t0,  {t0}(t0)",
+        "sret",
+        ra = const Registers::OFFSET_RA,
+        sp = const Registers::OFFSET_SP,
+        gp = const Registers::OFFSET_GP,
+        tp = const Registers::OFFSET_TP,
+        t1 = const Registers::OFFSET_T1,
+        t2 = const Registers::OFFSET_T2,
+        t0 = const Registers::OFFSET_T0,
+        a0 = const Registers::OFFSET_A0,
+        a1 = const Registers::OFFSET_A1,
+        a2 = const Registers::OFFSET_A2,
+        a3 = const Registers::OFFSET_A3,
+        a4 = const Registers::OFFSET_A4,
+        a5 = const Registers::OFFSET_A5,
+        a6 = const Registers::OFFSET_A6,
+        a7 = const Registers::OFFSET_A7,
+        t3 = const Registers::OFFSET_T3,
+        t4 = const Registers::OFFSET_T4,
+        t5 = const Registers::OFFSET_T5,
+        t6 = const Registers::OFFSET_T6,
+        s0 = const Registers::OFFSET_S0,
+        s1 = const Registers::OFFSET_S1,
+        s2 = const Registers::OFFSET_S2,
+        s3 = const Registers::OFFSET_S3,
+        s4 = const Registers::OFFSET_S4,
+        s5 = const Registers::OFFSET_S5,
+        s6 = const Registers::OFFSET_S6,
+        s7 = const Registers::OFFSET_S7,
+        s8 = const Registers::OFFSET_S8,
+        s9 = const Registers::OFFSET_S9,
+        s10 = const Registers::OFFSET_S10,
+        s11 = const Registers::OFFSET_S11,
+        sstatus = const TrapContext::OFFSET_SSTATUS,
+        sepc = const TrapContext::OFFSET_SEPC,
+    );
+}

+ 134 - 0
crates/eonix_hal/src/arch/riscv64/trap/default.rs

@@ -0,0 +1,134 @@
+use super::Registers;
+use crate::trap::TrapContext;
+use core::arch::naked_asm;
+
+unsafe extern "C" {
+    fn _default_trap_handler(trap_context: &mut TrapContext);
+}
+
+#[unsafe(naked)]
+pub(super) unsafe extern "C" fn _default_trap_entry() -> ! {
+    naked_asm!(
+        "csrrw t0,      sscratch, t0",
+        "sd    tp,      {tp}(t0)",
+        "sd    ra,      {ra}(t0)",
+        "sd    sp,      {sp}(t0)",
+        "sd    gp,      {gp}(t0)",
+        "sd    a0,      {a0}(t0)",
+        "sd    a1,      {a1}(t0)",
+        "sd    a2,      {a2}(t0)",
+        "sd    a3,      {a3}(t0)",
+        "sd    a4,      {a4}(t0)",
+        "sd    t1,      {t1}(t0)",
+        "sd    a5,      {a5}(t0)",
+        "sd    a6,      {a6}(t0)",
+        "sd    a7,      {a7}(t0)",
+        "sd    t3,      {t3}(t0)",
+        "sd    t4,      {t4}(t0)",
+        "sd    t5,      {t5}(t0)",
+        "sd    t2,      {t2}(t0)",
+        "sd    t6,      {t6}(t0)",
+        "sd    s0,      {s0}(t0)",
+        "sd    s1,      {s1}(t0)",
+        "sd    s2,      {s2}(t0)",
+        "sd    s3,      {s3}(t0)",
+        "sd    s4,      {s4}(t0)",
+        "sd    s5,      {s5}(t0)",
+        "sd    s6,      {s6}(t0)",
+        "sd    s7,      {s7}(t0)",
+        "sd    s8,      {s8}(t0)",
+        "sd    s9,      {s9}(t0)",
+        "sd    s10,     {s10}(t0)",
+        "sd    s11,     {s11}(t0)",
+        "mv    a0,      t0",
+        "csrrw t0,      sscratch, t0",
+        "sd    t0,      {t0}(a0)",
+        "csrr  t0,      sepc",
+        "csrr  t1,      scause",
+        "csrr  t2,      sstatus",
+        "csrr  t3,      stval",
+        "sd    t0,      {sepc}(a0)",
+        "sd    t1,      {scause}(a0)",
+        "sd    t2,      {sstatus}(a0)",
+        "sd    t3,      {stval}(a0)",
+
+        "la    t0,      {default_trap_handler}",
+        "jalr  t0",
+
+        "csrr  t0,      sscratch",
+        "ld    t1,      {sepc}(t0)",
+        "ld    t2,      {sstatus}(t0)",
+        "ld    tp,      {tp}(t0)",
+        "ld    ra,      {ra}(t0)",
+        "ld    sp,      {sp}(t0)",
+        "ld    gp,      {gp}(t0)",
+        "ld    a0,      {a0}(t0)",
+        "ld    a1,      {a1}(t0)",
+        "ld    a2,      {a2}(t0)",
+        "ld    a3,      {a3}(t0)",
+        "ld    a4,      {a4}(t0)",
+
+        "csrw  sepc,    t1",
+        "csrw  sstatus, t2",
+
+        "ld    t1,      {t1}(t0)",
+        "ld    a5,      {a5}(t0)",
+        "ld    a6,      {a6}(t0)",
+        "ld    a7,      {a7}(t0)",
+        "ld    t3,      {t3}(t0)",
+        "ld    t4,      {t4}(t0)",
+        "ld    t5,      {t5}(t0)",
+        "ld    t2,      {t2}(t0)",
+        "ld    t6,      {t6}(t0)",
+        "ld    s0,      {s0}(t0)",
+        "ld    s1,      {s1}(t0)",
+        "ld    s2,      {s2}(t0)",
+        "ld    s3,      {s3}(t0)",
+        "ld    s4,      {s4}(t0)",
+        "ld    s5,      {s5}(t0)",
+        "ld    s6,      {s6}(t0)",
+        "ld    s7,      {s7}(t0)",
+        "ld    s8,      {s8}(t0)",
+        "ld    s9,      {s9}(t0)",
+        "ld    s10,     {s10}(t0)",
+        "ld    s11,     {s11}(t0)",
+        "ld    t0,      {t0}(t0)",
+        "sret",
+        tp = const Registers::OFFSET_TP,
+        ra = const Registers::OFFSET_RA,
+        sp = const Registers::OFFSET_SP,
+        gp = const Registers::OFFSET_GP,
+        t0 = const Registers::OFFSET_T0,
+        t1 = const Registers::OFFSET_T1,
+        t2 = const Registers::OFFSET_T2,
+        t3 = const Registers::OFFSET_T3,
+        t4 = const Registers::OFFSET_T4,
+        t5 = const Registers::OFFSET_T5,
+        t6 = const Registers::OFFSET_T6,
+        a0 = const Registers::OFFSET_A0,
+        a1 = const Registers::OFFSET_A1,
+        a2 = const Registers::OFFSET_A2,
+        a3 = const Registers::OFFSET_A3,
+        a4 = const Registers::OFFSET_A4,
+        a5 = const Registers::OFFSET_A5,
+        a6 = const Registers::OFFSET_A6,
+        a7 = const Registers::OFFSET_A7,
+        s0 = const Registers::OFFSET_S0,
+        s1 = const Registers::OFFSET_S1,
+        s2 = const Registers::OFFSET_S2,
+        s3 = const Registers::OFFSET_S3,
+        s4 = const Registers::OFFSET_S4,
+        s5 = const Registers::OFFSET_S5,
+        s6 = const Registers::OFFSET_S6,
+        s7 = const Registers::OFFSET_S7,
+        s8 = const Registers::OFFSET_S8,
+        s9 = const Registers::OFFSET_S9,
+        s10 = const Registers::OFFSET_S10,
+        s11 = const Registers::OFFSET_S11,
+        sepc = const TrapContext::OFFSET_SEPC,
+        scause = const TrapContext::OFFSET_SCAUSE,
+        sstatus = const TrapContext::OFFSET_SSTATUS,
+        stval = const TrapContext::OFFSET_STVAL,
+        default_trap_handler = sym _default_trap_handler,
+    );
+}

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

@@ -0,0 +1,115 @@
+mod captured;
+mod default;
+mod trap_context;
+
+use super::config::platform::virt::*;
+use super::context::TaskContext;
+use captured::{_captured_trap_entry, _captured_trap_return};
+use core::arch::{global_asm, naked_asm};
+use core::mem::{offset_of, size_of};
+use core::num::NonZero;
+use core::ptr::NonNull;
+use default::_default_trap_entry;
+use eonix_hal_traits::{
+    context::RawTaskContext,
+    trap::{IrqState as IrqStateTrait, TrapReturn},
+};
+use riscv::register::sstatus::{self, Sstatus};
+use riscv::register::stvec::TrapMode;
+use riscv::register::{scause, sepc, sscratch, stval};
+use riscv::{
+    asm::sfence_vma_all,
+    register::stvec::{self, Stvec},
+};
+use sbi::SbiError;
+
+pub use trap_context::*;
+
+impl TrapReturn for TrapContext {
+    type TaskContext = TaskContext;
+
+    unsafe fn trap_return(&mut self) {
+        let irq_states = disable_irqs_save();
+
+        let old_stvec = stvec::read();
+        stvec::write({
+            let mut stvec_val = Stvec::from_bits(0);
+            stvec_val.set_address(_captured_trap_entry as usize);
+            stvec_val.set_trap_mode(TrapMode::Direct);
+            stvec_val
+        });
+
+        let old_trap_ctx = sscratch::read();
+        sscratch::write(&raw mut *self as usize);
+
+        let mut from_ctx = TaskContext::new();
+        let mut to_ctx = TaskContext::new();
+        to_ctx.set_program_counter(_captured_trap_return as usize);
+        to_ctx.set_stack_pointer(&raw mut from_ctx as usize);
+        to_ctx.set_interrupt_enabled(false);
+
+        unsafe {
+            TaskContext::switch(&mut from_ctx, &mut to_ctx);
+        }
+
+        sscratch::write(old_trap_ctx);
+        stvec::write(old_stvec);
+
+        irq_states.restore();
+    }
+}
+
+fn setup_trap_handler(trap_entry_addr: usize) {
+    let mut stvec_val = Stvec::from_bits(0);
+    stvec_val.set_address(trap_entry_addr);
+    stvec_val.set_trap_mode(TrapMode::Direct);
+
+    unsafe {
+        stvec::write(stvec_val);
+    }
+}
+
+pub fn setup_trap() {
+    setup_trap_handler(_default_trap_entry as usize);
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct IrqState(Sstatus);
+
+impl IrqState {
+    #[inline]
+    pub fn save() -> Self {
+        IrqState(sstatus::read())
+    }
+}
+
+impl IrqStateTrait for IrqState {
+    fn restore(self) {
+        let Self(state) = self;
+        unsafe {
+            sstatus::write(state);
+        }
+    }
+}
+
+#[inline]
+pub fn disable_irqs() {
+    unsafe {
+        sstatus::clear_sie();
+    }
+}
+
+#[inline]
+pub fn enable_irqs() {
+    unsafe {
+        sstatus::set_sie();
+    }
+}
+
+#[inline]
+pub fn disable_irqs_save() -> IrqState {
+    let state = IrqState::save();
+    disable_irqs();
+
+    state
+}

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

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

+ 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)
+    )
+}

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

@@ -0,0 +1,361 @@
+use crate::{
+    arch::{
+        bootstrap::{EARLY_GDT_DESCRIPTOR, KERNEL_PML4},
+        cpu::{wrmsr, CPU},
+        io::Port8,
+        mm::{ArchPhysAccess, GLOBAL_PAGE_TABLE, V_KERNEL_BSS_START},
+    },
+    bootstrap::BootStrapData,
+    mm::{ArchMemory, ArchPagingMode, BasicPageAlloc, BasicPageAllocRef, ScopedAllocator},
+};
+use acpi::{platform::ProcessorState, AcpiHandler, AcpiTables, PhysicalMapping, PlatformInfo};
+use 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.
+
+    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(4, 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);
+    }
+}

+ 113 - 0
crates/eonix_hal/src/arch/x86_64/context.rs

@@ -0,0 +1,113 @@
+use core::arch::naked_asm;
+use eonix_hal_traits::context::RawTaskContext;
+
+/// Necessary hardware states of task for doing context switches.
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct TaskContext {
+    r12: u64,
+    r13: u64,
+    r14: u64,
+    r15: u64,
+    rbx: u64,
+    rbp: u64,
+    rsp: u64,
+    rip: u64,    // Should we save rip here?
+    rflags: u64, // Should we save rflags here?
+}
+
+impl TaskContext {
+    /// Create a new task context with the given entry point and stack pointer.
+    /// The entry point is the function to be called when the task is scheduled.
+    /// The stack pointer is the address of the top of the stack.
+    /// The stack pointer should be aligned to 16 bytes.
+    pub const fn new() -> Self {
+        Self {
+            r12: 0,
+            r13: 0,
+            r14: 0,
+            r15: 0,
+            rbx: 0,
+            rbp: 0,
+            rsp: 0,
+            rip: 0,
+            rflags: 0x200, // IF = 1 by default.
+        }
+    }
+
+    #[unsafe(naked)]
+    unsafe extern "C" fn do_call() -> ! {
+        naked_asm!(
+            "mov %r12, %rdi",
+            "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(naked)]
+    unsafe extern "C" fn switch(from: &mut Self, to: &mut Self) {
+        naked_asm!(
+            "pop %rax",
+            "pushf",
+            "pop %rcx",
+            "mov %r12, (%rdi)",
+            "mov %r13, 8(%rdi)",
+            "mov %r14, 16(%rdi)",
+            "mov %r15, 24(%rdi)",
+            "mov %rbx, 32(%rdi)",
+            "mov %rbp, 40(%rdi)",
+            "mov %rsp, 48(%rdi)",
+            "mov %rax, 56(%rdi)",
+            "mov %rcx, 64(%rdi)",
+            "",
+            "mov (%rsi), %r12",
+            "mov 8(%rsi), %r13",
+            "mov 16(%rsi), %r14",
+            "mov 24(%rsi), %r15",
+            "mov 32(%rsi), %rbx",
+            "mov 40(%rsi), %rbp",
+            "mov 48(%rsi), %rdi", // store next stack pointer
+            "mov 56(%rsi), %rax",
+            "mov 64(%rsi), %rcx",
+            "push %rcx",
+            "popf",
+            "xchg %rdi, %rsp", // switch to new stack
+            "jmp *%rax",
+            options(att_syntax),
+        );
+    }
+}

+ 225 - 0
crates/eonix_hal/src/arch/x86_64/cpu.rs

@@ -0,0 +1,225 @@
+use super::gdt::{GDTEntry, GDT};
+use super::interrupt::InterruptControl;
+use super::trap::TrapContext;
+use core::arch::asm;
+use core::marker::PhantomPinned;
+use core::mem::size_of;
+use core::pin::Pin;
+use eonix_preempt::PreemptGuard;
+use eonix_sync_base::LazyLock;
+
+#[eonix_percpu::define_percpu]
+static LOCAL_CPU: LazyLock<CPU> = LazyLock::new(CPU::new);
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+#[allow(non_camel_case_types)]
+struct TSS_SP {
+    low: u32,
+    high: u32,
+}
+
+#[repr(C)]
+pub(crate) struct TSS {
+    _reserved1: u32,
+    rsp: [TSS_SP; 3],
+    _reserved2: u32,
+    _reserved3: u32,
+    ist: [TSS_SP; 7],
+    _reserved4: u32,
+    _reserved5: u32,
+    _reserved6: u16,
+    iomap_base: u16,
+    _pinned: PhantomPinned,
+}
+
+#[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.
+pub struct CPU {
+    cpuid: usize,
+    gdt: GDT,
+    tss: TSS,
+    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 {
+    pub fn new() -> Self {
+        let (interrupt_control, cpuid) = InterruptControl::new();
+
+        Self {
+            cpuid,
+            gdt: GDT::new(),
+            tss: TSS::new(),
+            interrupt: interrupt_control,
+        }
+    }
+
+    /// Load GDT and TSS in place.
+    ///
+    /// # Safety
+    /// Make sure preemption and interrupt are disabled before calling this function.
+    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 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.
+        interrupt.as_mut().setup_idt();
+        interrupt.as_mut().setup_timer();
+    }
+
+    /// Bootstrap all CPUs.
+    /// This should only be called on the BSP.
+    pub unsafe fn bootstrap_cpus(&self) {
+        self.interrupt.send_sipi();
+    }
+
+    pub unsafe fn load_interrupt_stack(self: Pin<&mut Self>, rsp: u64) {
+        unsafe {
+            self.map_unchecked_mut(|me| &mut me.tss)
+                .set_rsp0(rsp + size_of::<TrapContext>() as u64);
+        }
+    }
+
+    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;
+        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;
+        }
+    }
+}
+
+#[inline(always)]
+pub fn halt() {
+    unsafe {
+        asm!("hlt", options(att_syntax, nostack));
+    }
+}
+
+#[inline(always)]
+pub fn rdmsr(msr: u32) -> u64 {
+    let edx: u32;
+    let eax: u32;
+
+    unsafe {
+        asm!(
+            "rdmsr",
+            in("ecx") msr,
+            out("eax") eax,
+            out("edx") edx,
+            options(att_syntax),
+        );
+    }
+
+    (edx as u64) << 32 | eax as u64
+}
+
+#[inline(always)]
+pub fn wrmsr(msr: u32, value: u64) {
+    let eax = value as u32;
+    let edx = (value >> 32) as u32;
+
+    unsafe {
+        asm!(
+            "wrmsr",
+            in("ecx") msr,
+            in("eax") eax,
+            in("edx") edx,
+            options(att_syntax),
+        );
+    }
+}

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

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

+ 32 - 0
crates/eonix_hal/src/arch/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)
+            )
+        }
+    }
+}

+ 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),
+            );
+        }
     }
 }

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

@@ -0,0 +1,241 @@
+use crate::arch::cpu::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(0x0040);
+
+        // Setup the PIT to generate interrupts at 100Hz.
+        unsafe {
+            asm!(
+                "in $0x61, %al",
+                "and $0xfd, %al",
+                "or $0x1, %al",
+                "out %al, %dx",
+                "mov $0xb2, %al",
+                "out %al, $0x43",
+                "mov $0x9b, %al",
+                "out %al, $0x42",
+                "in $0x60, %al",
+                "mov $0x2e, %al",
+                "out %al, $0x42",
+                "in $0x61, %al",
+                "and $0xfe, %al",
+                "out %al, $0x61",
+                "or $0x1, %al",
+                "out %al, $0x61",
+                out("eax") _,
+                out("edx") _,
+                options(att_syntax, nomem, nostack, preserves_flags),
+            );
+        }
+
+        self.apic_base.timer_initial_count().write(u32::MAX);
+
+        unsafe {
+            asm!(
+                "2:",
+                "in $0x61, %al",
+                "and $0x20, %al",
+                "jz 2b",
+                out("ax") _,
+                options(att_syntax, nomem, nostack, preserves_flags),
+            )
+        }
+
+        self.apic_base.timer_register().write(0x10000);
+
+        let counts = self.apic_base.timer_current_count().read();
+        let freq = (u32::MAX - counts) as u64 * 16 * 100;
+
+        self.apic_base
+            .timer_initial_count()
+            .write((freq / 16 / 1_000) as u32);
+
+        self.apic_base.timer_register().write(0x20040);
+        self.apic_base.timer_divide().write(0x3); // Divide by 16
+    }
+
+    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 {
+            core::hint::spin_loop();
+        }
+
+        icr.write(0xc4606);
+        while icr.read() & 0x1000 != 0 {
+            core::hint::spin_loop();
+        }
+    }
+
+    /// 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));
+    }
+}

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

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

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

@@ -0,0 +1,109 @@
+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 {
+    .vdso ALIGN(0x1000) : ALIGN(0x1000)
+    {
+        KEEP(*(.vdso .vdso.*));
+
+        . = ALIGN(0x1000);
+    } > VDSO AT> REGION_TEXT
+
+    VDSO_PADDR = LOADADDR(.vdso);
+}
+INSERT AFTER .text;
+
+SECTIONS {
+    .text.syscall_fns :
+    {
+
+        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 = .;
+
+        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;

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

@@ -0,0 +1,18 @@
+MEMORY {
+    LOWMEM : org = 0x0000000000000000, len = 1M
+    VDSO   : org = 0x00007f0000000000, len = 4K
+    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);
+
+REGION_ALIAS("LINK_REGION_TEXT", KIMAGE);
+REGION_ALIAS("LINK_REGION_RODATA", KIMAGE);
+REGION_ALIAS("LINK_REGION_DATA", KIMAGE);
+REGION_ALIAS("LINK_REGION_BSS", KBSS);
+REGION_ALIAS("LINK_REGION_EHFRAME", KIMAGE);

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

@@ -0,0 +1,422 @@
+use crate::traits::mm::Memory;
+use core::{arch::asm, 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 {
+            return None;
+        }
+
+        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
+                    }
+                }),
+        )
+    }
+}
+
+#[inline(always)]
+pub fn flush_tlb(vaddr: usize) {
+    unsafe {
+        asm!(
+            "invlpg ({})",
+            in(reg) vaddr,
+            options(att_syntax)
+        );
+    }
+}
+
+#[inline(always)]
+pub fn flush_tlb_all() {
+    unsafe {
+        asm!(
+            "mov %cr3, %rax",
+            "mov %rax, %cr3",
+            out("rax") _,
+            options(att_syntax)
+        );
+    }
+}
+
+#[inline(always)]
+pub fn get_root_page_table_pfn() -> PFN {
+    let cr3: usize;
+    unsafe {
+        asm!(
+            "mov %cr3, {0}",
+            out(reg) cr3,
+            options(att_syntax)
+        );
+    }
+    PFN::from(PAddr::from(cr3))
+}
+
+#[inline(always)]
+pub fn set_root_page_table_pfn(pfn: PFN) {
+    unsafe {
+        asm!(
+            "mov {0}, %cr3",
+            in(reg) PAddr::from(pfn).addr(),
+            options(att_syntax)
+        );
+    }
+}

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

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

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

@@ -0,0 +1,392 @@
+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 {
+    type TaskContext = TaskContext;
+
+    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)
+}

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

@@ -0,0 +1,199 @@
+use crate::processor::CPU;
+use core::arch::asm;
+use eonix_hal_traits::{
+    fault::{Fault, PageFaultErrorCode},
+    trap::{RawTrapContext, TrapType},
+};
+use eonix_mm::address::VAddr;
+
+#[derive(Clone, Copy, Default)]
+#[repr(C, align(16))]
+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 & 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;
+                }
+
+                #[inline(always)]
+                fn get_page_fault_address() -> VAddr {
+                    let cr2: usize;
+                    unsafe {
+                        asm!(
+                            "mov %cr2, {}",
+                            out(reg) cr2,
+                            options(att_syntax)
+                        );
+                    }
+                    VAddr::from(cr2)
+                }
+
+                Fault::PageFault {
+                    error_code,
+                    address: get_page_fault_address(),
+                }
+            }
+            code @ 0..0x20 => Fault::Unknown(code as usize),
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl RawTrapContext for TrapContext {
+    type FIrq = impl FnOnce(fn(irqno: usize));
+    type FTimer = fn(handler: fn());
+
+    fn new() -> Self {
+        Self {
+            ..Default::default()
+        }
+    }
+
+    fn trap_type(&self) -> TrapType<Self::FIrq, Self::FTimer> {
+        match self.int_no {
+            0..0x20 => TrapType::Fault(self.get_fault_type()),
+            0x40 => TrapType::Timer {
+                callback: |handler| {
+                    CPU::local().as_mut().end_of_interrupt();
+                    handler();
+                },
+            },
+            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 {
+                callback: move |handler| {
+                    let irqno = no as usize - 0x20;
+
+                    use crate::arch::io::Port8;
+
+                    const PIC1_COMMAND: Port8 = Port8::new(0x20);
+                    const PIC2_COMMAND: Port8 = Port8::new(0xA0);
+
+                    PIC1_COMMAND.write(0x20); // EOI
+                    if irqno >= 8 {
+                        PIC2_COMMAND.write(0x20); // EOI
+                    }
+
+                    handler(irqno);
+                },
+            },
+        }
+    }
+
+    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;
+    }
+
+    fn set_user_call_frame<E>(
+        &mut self,
+        pc: usize,
+        sp: Option<usize>,
+        ra: Option<usize>,
+        args: &[usize],
+        write_memory: impl Fn(VAddr, &[u8]) -> Result<(), E>,
+    ) -> Result<(), E> {
+        self.set_program_counter(pc);
+
+        let mut sp = sp.unwrap_or_else(|| self.get_stack_pointer());
+
+        let arg_size = args.len() * 4;
+
+        sp -= arg_size;
+        sp &= !0xf; // Align to 16 bytes
+        for (idx, arg) in args.iter().enumerate() {
+            write_memory(VAddr::from(sp + idx * 8), &arg.to_ne_bytes())?;
+        }
+
+        if let Some(ra) = ra {
+            sp -= 4; // Space for return address
+            write_memory(VAddr::from(sp), &ra.to_ne_bytes())?;
+        }
+
+        self.set_stack_pointer(sp);
+        Ok(())
+    }
+}

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

@@ -0,0 +1,32 @@
+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
+    }
+}
+
+#[cfg(target_arch = "riscv64")]
+pub fn early_console_write(s: &str) {
+    crate::arch::bootstrap::early_console_write(s);
+}
+
+#[cfg(target_arch = "riscv64")]
+pub fn early_console_putchar(ch: u8) {
+    crate::arch::bootstrap::early_console_putchar(ch);
+}

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

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

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

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

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

@@ -0,0 +1,111 @@
+PROVIDE(_stext = ORIGIN(REGION_TEXT));
+
+SECTIONS {
+    .text _stext :
+    {
+        PROVIDE(__kernel_start = .);
+        __stext = .;
+
+        *(.text.entry);
+        *(.text .text.*);
+
+    } > REGION_TEXT AT> LINK_REGION_TEXT
+
+    __etext = .;
+
+    .rodata : ALIGN(16)
+    {
+        __srodata = .;
+
+        *(.rodata .rodata.*);
+        
+        . = ALIGN(8);
+
+        PROVIDE(__eh_frame = .);
+        PROVIDE(__executable_start = __stext);
+
+        KEEP(*(.eh_frame_hdr));
+        KEEP(*(.eh_frame));
+        KEEP(*(.eh_frame.*));
+
+    } > REGION_RODATA AT> LINK_REGION_RODATA
+
+    __erodata = .;
+
+    .data : ALIGN(16)
+    {
+        __sdata = .;
+
+        *(.data .data.*);
+        *(.got .got.plt);
+
+    } > REGION_DATA AT> LINK_REGION_DATA
+
+    .data.after :
+    {
+        __data_after = .;
+    } > REGION_DATA AT> LINK_REGION_DATA
+
+    __edata = .;
+
+    .bss (NOLOAD) : ALIGN(16)
+    {
+        __sbss = .;
+
+        *(.bss .bss.*);
+
+        . = ALIGN(0x1000);
+    } > REGION_BSS AT> LINK_REGION_BSS
+
+    __ebss = .;
+
+    . = ALIGN(0x1000);
+}
+
+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*)
+    }
+}

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

@@ -0,0 +1,201 @@
+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::{
+    flush_tlb, flush_tlb_all, get_root_page_table_pfn, set_root_page_table_pfn, ArchMemory,
+    ArchPagingMode, ArchPhysAccess, GLOBAL_PAGE_TABLE,
+};
+
+pub struct BasicPageAlloc {
+    ranges: [Option<PRange>; Self::MAX],
+    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
+            );
+        }
+    }
+}

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