1
0

356 Commits bad164e483 ... 755b006871

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

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff