summaryrefslogtreecommitdiff
path: root/na
diff options
context:
space:
mode:
Diffstat (limited to 'na')
-rw-r--r--na/.clang-format35
-rw-r--r--na/.clang-tidy23
-rw-r--r--na/Makefile419
-rw-r--r--na/arch/riscv/config.cc276
-rw-r--r--na/arch/riscv/defconfig39
-rw-r--r--na/arch/riscv/include/arch/config.h106
-rw-r--r--na/arch/riscv/include/arch/context.h33
-rw-r--r--na/arch/riscv/include/arch/cpu.h95
-rw-r--r--na/arch/riscv/include/arch/extable.h55
-rw-r--r--na/arch/riscv/include/arch/fpstate.h94
-rw-r--r--na/arch/riscv/include/arch/mmu.h78
-rw-r--r--na/arch/riscv/include/arch/pmap.h49
-rw-r--r--na/arch/riscv/include/arch/random.h27
-rw-r--r--na/arch/riscv/include/arch/sbi.h111
-rw-r--r--na/arch/riscv/include/arch/tlb.h19
-rw-r--r--na/arch/riscv/include/arch/trap.h94
-rw-r--r--na/arch/riscv/include/arch/trapframe.h140
-rw-r--r--na/arch/riscv/include/asm/csr.h37
-rw-r--r--na/arch/riscv/include/asm/page.h62
-rw-r--r--na/arch/riscv/include/asm/pgtable.h55
-rw-r--r--na/arch/riscv/include/asm/sections.h50
-rw-r--r--na/arch/riscv/kernel/asm-offsets.c29
-rw-r--r--na/arch/riscv/kernel/head.S317
-rw-r--r--na/arch/riscv/kernel/lumeos.lds.S120
-rw-r--r--na/arch/riscv/kernel/signal.cc163
-rw-r--r--na/arch/riscv/lib/random.cc21
-rw-r--r--na/arch/riscv/mm/init.cc241
-rw-r--r--na/arch/riscv/mm/pmap.cc361
-rw-r--r--na/arch/riscv/mm/tlb_md.cc17
-rw-r--r--na/arch/riscv/module.mk18
-rw-r--r--na/arch/riscv/syscall/syscall_md.cc42
-rw-r--r--na/arch/riscv/trap/extable.cc92
-rw-r--r--na/arch/riscv/trap/fpu.S103
-rw-r--r--na/arch/riscv/trap/swtch.S96
-rw-r--r--na/arch/riscv/trap/trap_md.cc241
-rw-r--r--na/arch/riscv/trap/trap_vector.S283
-rw-r--r--na/arch/riscv/uaccess/uaccess_memcpy.S117
-rw-r--r--na/drivers/base/bus.cc32
-rw-r--r--na/drivers/base/core.cc156
-rw-r--r--na/drivers/base/driver.cc72
-rw-r--r--na/drivers/base/module.mk9
-rw-r--r--na/drivers/base/platform.cc231
-rw-r--r--na/drivers/console.cc119
-rw-r--r--na/drivers/module.mk11
-rw-r--r--na/drivers/plic.cc259
-rw-r--r--na/drivers/tty/module.mk5
-rw-r--r--na/drivers/tty/n_tty.cc539
-rw-r--r--na/drivers/tty/tty_core.cc536
-rw-r--r--na/drivers/tty/tty_uart.cc251
-rw-r--r--na/drivers/uart.cc181
-rw-r--r--na/fdt/device_node.cc166
-rw-r--r--na/fdt/fdt.cc625
-rw-r--r--na/fdt/module.mk3
-rw-r--r--na/fs/chardev.cc43
-rw-r--r--na/fs/dcache.cc287
-rw-r--r--na/fs/devfs.cc137
-rw-r--r--na/fs/fd.cc216
-rw-r--r--na/fs/file_table.cc164
-rw-r--r--na/fs/fs_struct.cc90
-rw-r--r--na/fs/initramfs.cc285
-rw-r--r--na/fs/inode.cc166
-rw-r--r--na/fs/module.mk17
-rw-r--r--na/fs/namei.cc250
-rw-r--r--na/fs/pipe.cc278
-rw-r--r--na/fs/ramfs/file.cc216
-rw-r--r--na/fs/ramfs/inode.cc164
-rw-r--r--na/fs/ramfs/super.cc154
-rw-r--r--na/fs/super.cc95
-rw-r--r--na/fs/vfs_init.cc78
-rw-r--r--na/include/libc-shim/bits/alltypes.h15
-rw-r--r--na/include/libc-shim/endian.h9
-rw-r--r--na/include/libc-shim/features.h12
-rw-r--r--na/include/libc-shim/limits.h17
-rw-r--r--na/include/libc-shim/stdint.h21
-rw-r--r--na/include/libc-shim/string.h110
-rw-r--r--na/include/libc-shim/strings.h40
-rw-r--r--na/include/lume/addr.h91
-rw-r--r--na/include/lume/addr_types.h29
-rw-r--r--na/include/lume/args.h28
-rw-r--r--na/include/lume/atomic.h76
-rw-r--r--na/include/lume/bitops.h120
-rw-r--r--na/include/lume/chardev.h45
-rw-r--r--na/include/lume/clone.h34
-rw-r--r--na/include/lume/compiler.h54
-rw-r--r--na/include/lume/config.h100
-rw-r--r--na/include/lume/console.h77
-rw-r--r--na/include/lume/container_of.h17
-rw-r--r--na/include/lume/dcache.h143
-rw-r--r--na/include/lume/device.h75
-rw-r--r--na/include/lume/device_node.h64
-rw-r--r--na/include/lume/driver.h24
-rw-r--r--na/include/lume/elf.h150
-rw-r--r--na/include/lume/errno.h186
-rw-r--r--na/include/lume/fdt.h85
-rw-r--r--na/include/lume/fdt_prop.h24
-rw-r--r--na/include/lume/fdtable.h49
-rw-r--r--na/include/lume/features.h40
-rw-r--r--na/include/lume/file.h132
-rw-r--r--na/include/lume/fs.h310
-rw-r--r--na/include/lume/fs_struct.h39
-rw-r--r--na/include/lume/fs_types.h197
-rw-r--r--na/include/lume/init.h33
-rw-r--r--na/include/lume/irq.h84
-rw-r--r--na/include/lume/kbuild.h40
-rw-r--r--na/include/lume/kernel_map.h55
-rw-r--r--na/include/lume/klog.h200
-rw-r--r--na/include/lume/kobject.h57
-rw-r--r--na/include/lume/kprintf.h39
-rw-r--r--na/include/lume/kref.h46
-rw-r--r--na/include/lume/libfdt.h8
-rw-r--r--na/include/lume/libfdt_env.h78
-rw-r--r--na/include/lume/limits.h48
-rw-r--r--na/include/lume/list.h346
-rw-r--r--na/include/lume/lumeos.lds.h193
-rw-r--r--na/include/lume/memblock.h53
-rw-r--r--na/include/lume/mm_layout.h73
-rw-r--r--na/include/lume/mman.h21
-rw-r--r--na/include/lume/mount.h52
-rw-r--r--na/include/lume/mutex.h63
-rw-r--r--na/include/lume/namei.h32
-rw-r--r--na/include/lume/new.h18
-rw-r--r--na/include/lume/page.h151
-rw-r--r--na/include/lume/page_cache.h82
-rw-r--r--na/include/lume/pgrp.h125
-rw-r--r--na/include/lume/pgtable_types.h69
-rw-r--r--na/include/lume/pid.h29
-rw-r--r--na/include/lume/pipe.h52
-rw-r--r--na/include/lume/platform_device.h64
-rw-r--r--na/include/lume/pmm.h108
-rw-r--r--na/include/lume/poll.h68
-rw-r--r--na/include/lume/ptrace.h76
-rw-r--r--na/include/lume/random.h38
-rw-r--r--na/include/lume/rbtree.h207
-rw-r--r--na/include/lume/rbtree_augmented.h252
-rw-r--r--na/include/lume/rbtree_types.h28
-rw-r--r--na/include/lume/sched.h102
-rw-r--r--na/include/lume/sched_class.h35
-rw-r--r--na/include/lume/selftest.h65
-rw-r--r--na/include/lume/setup.h42
-rw-r--r--na/include/lume/signal.h60
-rw-r--r--na/include/lume/signal_nr.h54
-rw-r--r--na/include/lume/signal_types.h263
-rw-r--r--na/include/lume/slab.h28
-rw-r--r--na/include/lume/smp.h74
-rw-r--r--na/include/lume/stdarg.h11
-rw-r--r--na/include/lume/stringify.h14
-rw-r--r--na/include/lume/syscall.h38
-rw-r--r--na/include/lume/syscall_nr.h82
-rw-r--r--na/include/lume/task.h144
-rw-r--r--na/include/lume/timer.h43
-rw-r--r--na/include/lume/tlb.h48
-rw-r--r--na/include/lume/trap.h109
-rw-r--r--na/include/lume/tty.h165
-rw-r--r--na/include/lume/tty_ioctl.h202
-rw-r--r--na/include/lume/types.h78
-rw-r--r--na/include/lume/uaccess.h27
-rw-r--r--na/include/lume/uuid.h207
-rw-r--r--na/include/lume/vmm.h115
-rw-r--r--na/include/lume/wait.h31
-rw-r--r--na/include/lume/waitqueue.h79
-rw-r--r--na/kernel/exec.cc659
-rw-r--r--na/kernel/irq.cc106
-rw-r--r--na/kernel/main.cc468
-rw-r--r--na/kernel/module.mk26
-rw-r--r--na/kernel/pgrp.cc148
-rw-r--r--na/kernel/pid.cc63
-rw-r--r--na/kernel/poll.cc250
-rw-r--r--na/kernel/ptrace.cc92
-rw-r--r--na/kernel/sched_class_o1.cc81
-rw-r--r--na/kernel/sched_core.cc571
-rw-r--r--na/kernel/sched_internal.h115
-rw-r--r--na/kernel/signal.cc442
-rw-r--r--na/kernel/smp.cc112
-rw-r--r--na/kernel/sync/cpu_state.h35
-rw-r--r--na/kernel/sync/mutex.cc156
-rw-r--r--na/kernel/sync/spinlock.cc120
-rw-r--r--na/kernel/sync/spinlock.h61
-rw-r--r--na/kernel/sys_fs.cc679
-rw-r--r--na/kernel/sys_info.cc49
-rw-r--r--na/kernel/sys_misc.cc360
-rw-r--r--na/kernel/sys_mm.cc178
-rw-r--r--na/kernel/sys_process.cc185
-rw-r--r--na/kernel/sys_signal.cc351
-rw-r--r--na/kernel/syscall.cc208
-rw-r--r--na/kernel/task.cc780
-rw-r--r--na/kernel/timer.cc118
-rw-r--r--na/kernel/trap.cc258
-rw-r--r--na/kernel/uaccess.cc53
-rw-r--r--na/kernel/waitqueue.cc144
-rw-r--r--na/lib/cxx_abi.cc37
-rw-r--r--na/lib/fdt.c2
-rw-r--r--na/lib/fdt_addresses.c2
-rw-r--r--na/lib/fdt_empty_tree.c2
-rw-r--r--na/lib/fdt_ro.c2
-rw-r--r--na/lib/fdt_rw.c2
-rw-r--r--na/lib/fdt_strerror.c2
-rw-r--r--na/lib/fdt_sw.c2
-rw-r--r--na/lib/fdt_wip.c2
-rw-r--r--na/lib/klog.cc574
-rw-r--r--na/lib/kobject.cc82
-rw-r--r--na/lib/kprintf.cc91
-rw-r--r--na/lib/module.mk66
-rw-r--r--na/lib/printf.c914
-rw-r--r--na/lib/printf.h129
-rw-r--r--na/lib/random.cc152
-rw-r--r--na/lib/rbtree.cc596
-rw-r--r--na/lib/string/COPYRIGHT193
-rw-r--r--na/lib/string/aarch64/memcpy.S186
-rw-r--r--na/lib/string/aarch64/memset.S115
-rw-r--r--na/lib/string/arm/__aeabi_memcpy.s45
-rw-r--r--na/lib/string/arm/__aeabi_memset.s31
-rw-r--r--na/lib/string/arm/memcpy.S479
-rw-r--r--na/lib/string/bcmp.c11
-rw-r--r--na/lib/string/bcopy.c11
-rw-r--r--na/lib/string/bzero.c11
-rw-r--r--na/lib/string/explicit_bzero.c11
-rw-r--r--na/lib/string/i386/memcpy.s32
-rw-r--r--na/lib/string/i386/memmove.s22
-rw-r--r--na/lib/string/i386/memset.s76
-rw-r--r--na/lib/string/index.c11
-rw-r--r--na/lib/string/memccpy.c37
-rw-r--r--na/lib/string/memchr.c30
-rw-r--r--na/lib/string/memcmp.c11
-rw-r--r--na/lib/string/memcpy.c127
-rw-r--r--na/lib/string/memmem.c152
-rw-r--r--na/lib/string/memmove.c45
-rw-r--r--na/lib/string/mempcpy.c10
-rw-r--r--na/lib/string/memrchr.c14
-rw-r--r--na/lib/string/memset.c93
-rw-r--r--na/lib/string/rindex.c11
-rw-r--r--na/lib/string/stpcpy.c32
-rw-r--r--na/lib/string/stpncpy.c35
-rw-r--r--na/lib/string/strcat.c10
-rw-r--r--na/lib/string/strchr.c10
-rw-r--r--na/lib/string/strchrnul.c31
-rw-r--r--na/lib/string/strcmp.c10
-rw-r--r--na/lib/string/strcpy.c10
-rw-r--r--na/lib/string/strcspn.c20
-rw-r--r--na/lib/string/strlcat.c12
-rw-r--r--na/lib/string/strlcpy.c37
-rw-r--r--na/lib/string/strlen.c25
-rw-r--r--na/lib/string/strncat.c13
-rw-r--r--na/lib/string/strncmp.c12
-rw-r--r--na/lib/string/strncpy.c10
-rw-r--r--na/lib/string/strnlen.c10
-rw-r--r--na/lib/string/strpbrk.c10
-rw-r--r--na/lib/string/strrchr.c9
-rw-r--r--na/lib/string/strsep.c16
-rw-r--r--na/lib/string/strspn.c23
-rw-r--r--na/lib/string/strstr.c157
-rw-r--r--na/lib/string/strtok.c16
-rw-r--r--na/lib/string/strtok_r.c15
-rw-r--r--na/lib/string/x86_64/memcpy.s25
-rw-r--r--na/lib/string/x86_64/memmove.s16
-rw-r--r--na/lib/string/x86_64/memset.s72
-rw-r--r--na/mm/memblock.cc257
-rw-r--r--na/mm/module.mk7
-rw-r--r--na/mm/page_cache.cc252
-rw-r--r--na/mm/pmm.cc528
-rw-r--r--na/mm/slab.cc321
-rw-r--r--na/mm/slab.h83
-rw-r--r--na/mm/tlb.cc90
-rw-r--r--na/mm/vma.cc640
-rw-r--r--na/mm/vmm.cc235
-rw-r--r--na/scripts/Kbuild.include110
-rw-r--r--na/scripts/Kconfig.mk238
-rw-r--r--na/scripts/dtc/libfdt/fdt.c347
-rw-r--r--na/scripts/dtc/libfdt/fdt.h66
-rw-r--r--na/scripts/dtc/libfdt/fdt_addresses.c101
-rw-r--r--na/scripts/dtc/libfdt/fdt_empty_tree.c38
-rw-r--r--na/scripts/dtc/libfdt/fdt_overlay.c1101
-rw-r--r--na/scripts/dtc/libfdt/fdt_ro.c888
-rw-r--r--na/scripts/dtc/libfdt/fdt_rw.c512
-rw-r--r--na/scripts/dtc/libfdt/fdt_strerror.c60
-rw-r--r--na/scripts/dtc/libfdt/fdt_sw.c384
-rw-r--r--na/scripts/dtc/libfdt/fdt_wip.c94
-rw-r--r--na/scripts/dtc/libfdt/libfdt.h2540
-rw-r--r--na/scripts/dtc/libfdt/libfdt_env.h69
-rw-r--r--na/scripts/dtc/libfdt/libfdt_internal.h202
-rw-r--r--na/scripts/extract-asm-offsets.sed18
-rwxr-xr-xna/scripts/gen_initramfs.sh250
-rw-r--r--na/test/module.mk30
-rw-r--r--na/test/selftest.cc83
-rw-r--r--na/test/test_atomic.cc102
-rw-r--r--na/test/test_dcache.cc232
-rw-r--r--na/test/test_driver_model.cc209
-rw-r--r--na/test/test_exec.cc447
-rw-r--r--na/test/test_fdt.cc50
-rw-r--r--na/test/test_klog.cc175
-rw-r--r--na/test/test_mutex.cc183
-rw-r--r--na/test/test_namei.cc173
-rw-r--r--na/test/test_page_cache.cc180
-rw-r--r--na/test/test_pid.cc106
-rw-r--r--na/test/test_pipe_devfs.cc420
-rw-r--r--na/test/test_pmm.cc283
-rw-r--r--na/test/test_random.cc49
-rw-r--r--na/test/test_rbtree.cc119
-rw-r--r--na/test/test_sched.cc257
-rw-r--r--na/test/test_signal.cc193
-rw-r--r--na/test/test_slab.cc255
-rw-r--r--na/test/test_smp.cc136
-rw-r--r--na/test/test_spinlock.cc135
-rw-r--r--na/test/test_sys_fs_extra.cc238
-rw-r--r--na/test/test_sys_mm.cc178
-rw-r--r--na/test/test_sys_process.cc93
-rw-r--r--na/test/test_trap.cc197
-rw-r--r--na/test/test_uaccess.cc97
-rw-r--r--na/test/test_vfs_io.cc345
-rw-r--r--na/test/test_vma.cc282
-rw-r--r--na/test/test_vmm.cc58
-rw-r--r--na/test/test_waitqueue.cc91
-rw-r--r--na/usr/default_cpio_list24
-rw-r--r--na/usr/gen_init_cpio.c781
313 files changed, 0 insertions, 44967 deletions
diff --git a/na/.clang-format b/na/.clang-format
deleted file mode 100644
index 141e1e2..0000000
--- a/na/.clang-format
+++ /dev/null
@@ -1,35 +0,0 @@
-Language: Cpp
-Standard: c++23
-BasedOnStyle: Google
-
-# Core Formatting
-IndentWidth: 4
-ColumnLimit: 100
-TabWidth: 4
-UseTab: Never
-
-# Pointers and References
-PointerAlignment: Left
-ReferenceAlignment: Left
-
-# Includes
-SortIncludes: true
-
-# Alignment
-AlignConsecutiveAssignments: false
-AlignConsecutiveDeclarations: false
-AlignConsecutiveMacros: true
-
-# Control Statements
-BreakBeforeBraces: Attach
-AllowShortIfStatementsOnASingleLine: false
-AllowShortLoopsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: Empty
-
-# Spaces
-SpaceBeforeParens: ControlStatements
-SpaceInEmptyParentheses: false
-SpacesInAngles: false
-SpacesInParentheses: false
-SpacesInSquareBrackets: false
-SpaceAfterTemplateKeyword: true
diff --git a/na/.clang-tidy b/na/.clang-tidy
deleted file mode 100644
index 1af2c5a..0000000
--- a/na/.clang-tidy
+++ /dev/null
@@ -1,23 +0,0 @@
-Checks: >
- -*,
- clang-analyzer-*,
- performance-*,
- bugprone-*,
- readability-*,
- misc-*,
- modernize-*,
- -modernize-use-trailing-return-type,
- -modernize-use-nodiscard,
- -readability-identifier-length
-
-WarningsAsErrors: ''
-HeaderFilterRegex: '.*'
-FormatStyle: 'file'
-CheckOptions:
- readability-identifier-naming.ClassCase: PascalCase
- readability-identifier-naming.StructCase: PascalCase
- readability-identifier-naming.VariableCase: lower_case
- readability-identifier-naming.FunctionCase: lower_case
- readability-identifier-naming.GlobalVariableCase: lower_case
- readability-identifier-naming.GlobalVariablePrefix: g_
- readability-identifier-naming.PrivateMemberSuffix: _
diff --git a/na/Makefile b/na/Makefile
deleted file mode 100644
index 88689f5..0000000
--- a/na/Makefile
+++ /dev/null
@@ -1,419 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (C) 2026 Richard Qin
-#
-# LumeOS Kernel Makefile (Non-Recursive)
-#
-# Usage:
-# make - Build kernel ELF
-# make run - Run in QEMU
-# make debug - Run in QEMU with GDB server
-# make clean - Clean build artifacts
-# make fmt - Format all source files
-# make disasm - Generate disassembly listing
-#
-# Reference: docs/specs/00_conventions.md §7
-
-# ============================================================
-# Build Configuration
-# ============================================================
-BUILD := build
-
-# Make sure `make` with no arguments builds the kernel image, not the
-# first rule encountered during makefile parsing. scripts/Kconfig.mk
-# (included next) defines `defconfig:` at the top of its rule list,
-# which would otherwise become the default goal and silently overwrite
-# .config from arch/$(ARCH)/defconfig.
-.DEFAULT_GOAL := all
-
-# Load Kbuild helpers (Q, $(call cmd,...), quiet_cmd_* / cmd_* templates,
-# kbuild-objs translator). Must come before Kconfig.mk so subsequent
-# rules can use $(call cmd,...) immediately.
-include scripts/Kbuild.include
-
-# Load Kconfig infrastructure (reads .config, sets KCPPFLAGS)
-include scripts/Kconfig.mk
-
-ARCH ?= $(CONFIG_ARCH)
-
-# Two boot products — same pair as Linux RISC-V's arch/riscv/boot/Makefile
-# (vmlinux + Image), but named LumeOS-style:
-# VMLUME -> build/vmlume.elf : ELF with .symtab/PT_DYNAMIC/.rela.dyn
-# IMAGE -> build/vmlume.bin : raw binary (objcopy -O binary)
-VMLUME := $(BUILD)/vmlume.elf
-IMAGE := $(BUILD)/vmlume.bin
-
-# Initramfs paths
-INITRAMFS_DIR := $(BUILD)/initramfs
-INITRAMFS_CPIO := $(BUILD)/initramfs.cpio
-INITRAMFS_OBJ := $(BUILD)/initramfs.o
-USR_INIT_ELF := $(BUILD)/usr/init
-# Relocations: linker emits .rela.dyn under -shared -Bsymbolic; boot
-# code in arch/riscv/mm/init.cc::relocate_kernel walks it at startup.
-LDS_SRC := arch/$(ARCH)/kernel/lumeos.lds.S
-LINKER_SCRIPT := $(BUILD)/lumeos.lds
-PYTHON ?= python3
-
-# ============================================================
-# Toolchain Detection
-# ============================================================
-ifndef TOOLCHAIN_PREFIX
-ifeq ($(ARCH),riscv)
- # Prefer the linux-gnu toolchain — it supports -shared / -pie which
- # are required to emit .rela.dyn (Linux RISC-V kernel uses the same).
- # The bare-metal `riscv64-unknown-elf-` is kept only as a last-resort
- # fallback; with that one, `-shared` fails ("not supported") and the
- # kernel can't relocate at runtime.
- TOOLCHAIN_PREFIX := $(shell \
- if command -v riscv64-unknown-linux-gnu-g++ >/dev/null 2>&1; then \
- echo riscv64-unknown-linux-gnu-; \
- elif command -v riscv64-linux-gnu-g++ >/dev/null 2>&1; then \
- echo riscv64-linux-gnu-; \
- else \
- echo riscv64-unknown-linux-gnu-; \
- fi)
-else ifeq ($(ARCH),aarch64)
- TOOLCHAIN_PREFIX := aarch64-linux-gnu-
-else ifeq ($(ARCH),x86_64)
- TOOLCHAIN_PREFIX := x86_64-linux-gnu-
-endif
-endif
-
-CC := $(TOOLCHAIN_PREFIX)gcc
-CXX := $(TOOLCHAIN_PREFIX)g++
-LD := $(TOOLCHAIN_PREFIX)ld
-OBJCOPY := $(TOOLCHAIN_PREFIX)objcopy
-OBJDUMP := $(TOOLCHAIN_PREFIX)objdump
-# Objcopy output formats for binary-to-ELF relocation blob
-OBJCOPY_ELF_riscv := elf64-littleriscv
-OBJCOPY_ELF_aarch64 := elf64-littleaarch64
-OBJCOPY_ELF_x86_64 := elf64-x86-64
-OBJCOPY_ELF := $(OBJCOPY_ELF_$(ARCH))
-
-OBJCOPY_BINARCH_riscv := riscv
-OBJCOPY_BINARCH_aarch64 := aarch64
-OBJCOPY_BINARCH_x86_64 := i386:x86-64
-OBJCOPY_BINARCH := $(OBJCOPY_BINARCH_$(ARCH))
-
-# Common flags (base, before module includes)
-WARNINGS := -Wall -Wextra -Wno-unused-parameter
-INCLUDES := -Iinclude -I.
-
-# ============================================================
-# Source Collection (Non-Recursive Module Fragments)
-# Module .mk files may append to SRCS_CC, SRCS_S, INCLUDES
-# ============================================================
-SRCS_CC :=
-SRCS_C :=
-SRCS_S :=
-SRCS_s :=
-
--include arch/$(ARCH)/module.mk
-include kernel/module.mk
-include mm/module.mk
-include fs/module.mk
-include drivers/base/module.mk
-include drivers/module.mk
-include drivers/tty/module.mk
-include fdt/module.mk
-include lib/module.mk
-include test/module.mk
-
-# Compiler flags (defined AFTER includes so INCLUDES is complete)
-CFLAGS := $(WARNINGS) -O2 -g \
- -ffreestanding -nostdlib \
- -mcmodel=medany \
- -fPIE \
- -MMD -MP \
- $(KCPPFLAGS) \
- $(INCLUDES) \
- -I$(BUILD)/generated
-
-# -fPIE makes the compiler emit position-independent code (PC-relative
-# addressing + GOT for globals), which is what `-shared` linking expects.
-# Mirrors Linux's arch/riscv/Makefile under CONFIG_RELOCATABLE=y:
-# KBUILD_CFLAGS += -fPIE
-
-# C flags for musl libc sources: add libc-shim headers first
-CFLAGS_LIBC := $(CFLAGS) -Iinclude/libc-shim \
- -Wno-parentheses -Wno-sign-compare
-
-CXXFLAGS := $(CFLAGS) \
- -std=c++17 \
- -fno-exceptions -fno-rtti -fno-use-cxa-atexit \
- -fno-threadsafe-statics
-
-# Assembler flags MUST NOT include -fPIE.
-# Linux's arch/riscv/Makefile adds -fPIE only to KBUILD_CFLAGS, NOT to
-# KBUILD_AFLAGS: with -fPIE the gcc driver puts the assembler in PIC
-# mode, where the `la` macro silently expands to a GOT-indirect load
-# (`auipc + ld` reading a slot that holds the link-time VA). In our
-# pre-MMU boot path that VA is unmapped, so any `la sym, sym` faults
-# and reboots into a `_start` loop. Stripping -fPIE makes `la` emit
-# direct PC-relative addressing (`auipc + addi`) which works at PA.
-ASFLAGS := $(filter-out -fPIE,$(CFLAGS))
-
-# Link as a position-independent shared object so the linker emits
-# `.rela.dyn` with R_RISCV_RELATIVE entries that the boot stub applies
-# at load time. Mirrors Linux's arch/riscv/Makefile §10:
-# LDFLAGS_vmlinux += -shared -Bsymbolic -z notext
-# `-Bsymbolic` resolves all global refs at link time (no dynamic
-# resolution); `-z notext` lets us keep relocations in code pages.
-LDFLAGS := -nostdlib -shared -Bsymbolic -z notext -z norelro --no-dynamic-linker
-
-# ============================================================
-# Object File Computation
-# ============================================================
-OBJS := $(patsubst %.cc,$(BUILD)/%.o,$(SRCS_CC)) \
- $(patsubst %.c,$(BUILD)/%.o,$(SRCS_C)) \
- $(patsubst %.S,$(BUILD)/%.o,$(SRCS_S)) \
- $(patsubst %.s,$(BUILD)/%.o,$(SRCS_s))
-
-DEPS := $(OBJS:.o=.d)
-
-# ============================================================
-# Build Rules
-# ============================================================
-.PHONY: all clean run debug fmt disasm defconfig savedefconfig showconfig
-
-all: $(VMLUME) $(IMAGE)
-
-# Generate autoconf.h before any compilation
-$(AUTOCONF_H): scripts/Kconfig.mk $(wildcard .config)
- $(generate-autoconf)
-
-# ============================================================
-# Generated header: asm-offsets.h
-#
-# Linux-style trick (mirrors include/linux/kbuild.h + arch/*/kernel/
-# asm-offsets.c + scripts/Makefile.build): compile asm-offsets.c to
-# assembly, scrape the magic `->SYM val` markers produced by the
-# DEFINE/OFFSET inline-asm macros, rewrite them into `#define`
-# directives via sed. head.S and friends then `#include
-# <generated/asm-offsets.h>` to access struct field offsets without
-# having to hand-maintain them — re-order a struct field, the
-# generated constant follows.
-# ============================================================
-ASM_OFFSETS_C := arch/$(ARCH)/kernel/asm-offsets.c
-ASM_OFFSETS_S := $(BUILD)/arch/$(ARCH)/kernel/asm-offsets.s
-ASM_OFFSETS_H := $(BUILD)/generated/asm-offsets.h
-ASM_OFFSETS_SED := scripts/extract-asm-offsets.sed
-
-# Compile asm-offsets.c to *.s only — we never want the .o linked in.
-$(ASM_OFFSETS_S): $(ASM_OFFSETS_C) $(AUTOCONF_H)
- @echo " CC -S $<"
- @mkdir -p $(dir $@)
- @$(CC) $(CFLAGS_LIBC) -S $< -o $@
-
-# Rewrite ->NAME markers into #define lines. Header guard hand-rolled
-# at the top, sed body in the middle, closing #endif at the bottom.
-$(ASM_OFFSETS_H): $(ASM_OFFSETS_S) $(ASM_OFFSETS_SED)
- @echo " GEN $@"
- @mkdir -p $(dir $@)
- @(echo "/* AUTO-GENERATED — DO NOT EDIT */"; \
- echo "/* Generated from $(ASM_OFFSETS_C) by scripts/extract-asm-offsets.sed */"; \
- echo "#ifndef _LUME_GENERATED_ASM_OFFSETS_H"; \
- echo "#define _LUME_GENERATED_ASM_OFFSETS_H"; \
- echo ""; \
- sed -n -f $(ASM_OFFSETS_SED) $<; \
- echo ""; \
- echo "#endif /* _LUME_GENERATED_ASM_OFFSETS_H */") > $@
-
-# All objects depend on autoconf.h and asm-offsets.h
-$(OBJS): $(AUTOCONF_H) $(ASM_OFFSETS_H)
-
-# ============================================================
-# Generic %.pi.o post-processing rule
-#
-# Mirrors arch/riscv/kernel/pi/Makefile §26-30 in Linux:
-#
-# $(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_
-# --remove-section=.note.gnu.property
-# --prefix-alloc-sections=.init.pi
-# $(obj)/%.pi.o: $(obj)/%.o FORCE
-# $(call if_changed,objcopy)
-#
-# Purpose: take a regular .o produced by the normal compile rule and
-# rewrite it so the early position-independent code can coexist with
-# the rest of the kernel without symbol clashes:
-#
-# --prefix-symbols=__pi_ Every global symbol becomes __pi_<name>.
-# head.S calls them as __pi_fdt_path_offset
-# etc., keeping the pre-MMU namespace
-# cleanly isolated.
-#
-# --prefix-alloc-sections=.init.pi
-# All SHF_ALLOC sections get re-prefixed
-# so they land in .init.pi in vmlinux.lds.S,
-# which positions them where the boot
-# loader has already mapped memory.
-#
-# --remove-section=.note.gnu.property
-# Discarded because post-rename it can
-# confuse the linker about ELF features.
-#
-# Generic pattern (`$(BUILD)/%.pi.o`) so any subdir's source can opt in
-# by listing a `.pi.o` target in its SRCS. The companion compile-side
-# CFLAGS overrides (replacing -fPIE with -fpie, adding -Os, etc.) are
-# expected to come from the subdir's own .mk fragment via target-
-# specific CFLAGS_LIBC / CXXFLAGS overrides.
-# ============================================================
-$(BUILD)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \
- --remove-section=.note.gnu.property \
- --prefix-alloc-sections=.init.pi
-
-$(BUILD)/%.pi.o: $(BUILD)/%.o
- $(call cmd,objcopy)
-
-# Preprocess linker script template (.lds.S → .lds)
-$(LINKER_SCRIPT): $(LDS_SRC) include/lume/lumeos.lds.h
- @echo " LDS $@"
- @mkdir -p $(dir $@)
- @$(CC) -E -P -x c $(INCLUDES) $< -o $@
-
-# ============================================================
-# Initramfs: pack a root filesystem into a newc cpio.
-#
-# Pipeline mirrors Linux's Kbuild:
-# usr/gen_init_cpio — host C tool, produces cpio bytes
-# scripts/gen_initramfs.sh — wrapper that prepares the listing,
-# handles directories vs. listing files,
-# applies uid/gid remap, then invokes the
-# host tool.
-# Sourced from $(CONFIG_INITRAMFS_SOURCE) — see scripts/Kconfig.mk.
-# ============================================================
-
-# Host compiler — distinct from $(CC), which may be the cross compiler.
-HOSTCC ?= cc
-HOSTCFLAGS ?= -O2 -Wall
-
-# Output path matches Linux convention (usr/gen_init_cpio) so the
-# verbatim copy of scripts/gen_initramfs.sh from Linux finds it via its
-# hardcoded relative path when invoked from the kernel source root.
-GEN_INIT_CPIO := usr/gen_init_cpio
-
-$(GEN_INIT_CPIO): usr/gen_init_cpio.c
- @echo " HOSTCC $@"
- @$(HOSTCC) $(HOSTCFLAGS) -o $@ $<
-
-INITRAMFS_SOURCE := $(CONFIG_INITRAMFS_SOURCE)
-
-# Record the chosen source so changing CONFIG_INITRAMFS_SOURCE on the
-# command line forces a rebuild — without this, `make` only compares
-# file mtimes and would happily reuse a cpio packed from a different
-# source as long as that other source isn't newer. Evaluated at
-# Makefile parse time so the dep edge is correct from the first rule
-# check. Mirrors the idea behind Linux's $(call if_changed,...).
-INITRAMFS_SRC_STAMP := $(BUILD)/.initramfs_src.stamp
-$(shell mkdir -p $(BUILD) 2>/dev/null; \
- prev=$$([ -f "$(INITRAMFS_SRC_STAMP)" ] && cat "$(INITRAMFS_SRC_STAMP)" || echo); \
- if [ "$$prev" != "$(INITRAMFS_SOURCE)" ]; then \
- printf '%s\n' "$(INITRAMFS_SOURCE)" > "$(INITRAMFS_SRC_STAMP)"; \
- fi)
-
-$(INITRAMFS_CPIO): $(INITRAMFS_SOURCE) $(INITRAMFS_SRC_STAMP) \
- $(GEN_INIT_CPIO) scripts/gen_initramfs.sh
- @echo " INITRAMFS $(INITRAMFS_SOURCE)"
- @mkdir -p $(dir $@)
- @./scripts/gen_initramfs.sh -o $@ \
- -u $(CONFIG_INITRAMFS_ROOT_UID) \
- -g $(CONFIG_INITRAMFS_ROOT_GID) \
- $(INITRAMFS_SOURCE)
-
-$(INITRAMFS_OBJ): $(INITRAMFS_CPIO)
- @echo " OBJCOPY $@ (initramfs)"
- @$(OBJCOPY) -I binary -O $(OBJCOPY_ELF) -B $(OBJCOPY_BINARCH) \
- --rename-section .data=.init.ramfs,alloc,load,readonly,data,contents \
- $< $@
-
-# Single-pass kernel link. With `-shared -Bsymbolic`, the linker
-# emits `.rela.dyn` containing R_RISCV_RELATIVE entries directly into
-# the output ELF — no separate gen_reloc pass.
-$(VMLUME): $(OBJS) $(INITRAMFS_OBJ) $(LINKER_SCRIPT)
- @echo " LD $@"
- @mkdir -p $(dir $@)
- @$(LD) $(LDFLAGS) -T $(LINKER_SCRIPT) -o $@ $(OBJS) $(INITRAMFS_OBJ)
- @$(TOOLCHAIN_PREFIX)size $@
-
-# Image = raw binary, mirrors Linux's `arch/riscv/boot/Image` (objcopy
-# vmlinux to raw bytes, no ELF header — what U-Boot or QEMU's `-bios`
-# bare-loading would consume). Linux always builds both vmlinux and
-# Image; LumeOS follows the same convention.
-$(IMAGE): $(VMLUME)
- @echo " OBJCOPY $@"
- @$(OBJCOPY) -O binary $< $@
-
-# Compile C++ sources. Recipe goes through $(call cmd,...) so that
-# `make V=1` echoes the full command line. $(Q) prefix on mkdir keeps
-# the directory-creation step silent in quiet mode but visible at V=1.
-$(BUILD)/%.o: %.cc
- $(Q)mkdir -p $(dir $@)
- $(call cmd,cxx_o_cc)
-
-$(BUILD)/%.o: %.c
- $(Q)mkdir -p $(dir $@)
- $(call cmd,cc_o_c)
-
-$(BUILD)/%.o: %.S
- $(Q)mkdir -p $(dir $@)
- $(call cmd,as_o_S)
-
-$(BUILD)/%.o: %.s
- $(Q)mkdir -p $(dir $@)
- $(call cmd,as_o_S)
-
-# Auto-dependency tracking
--include $(DEPS)
-
-# ============================================================
-# Run & Debug
-# ============================================================
-ifeq ($(ARCH),riscv)
-QEMU := qemu-system-riscv64
-QEMU_MACHINE := -machine virt -smp 4 -m 128M -nographic
-QEMU_BIOS := -bios default
-else ifeq ($(ARCH),aarch64)
-QEMU := qemu-system-aarch64
-QEMU_MACHINE := -machine virt -cpu cortex-a53 -m 128M -nographic
-QEMU_BIOS :=
-else ifeq ($(ARCH),x86_64)
-QEMU := qemu-system-x86_64
-QEMU_MACHINE := -m 128M -nographic
-QEMU_BIOS :=
-endif
-
-run: $(VMLUME) $(IMAGE)
- @echo " QEMU Running..."
- $(QEMU) $(QEMU_MACHINE) $(QEMU_BIOS) -kernel $(VMLUME)
-
-debug: $(VMLUME) $(IMAGE)
- @echo " QEMU Waiting for GDB on :1234..."
- $(QEMU) $(QEMU_MACHINE) $(QEMU_BIOS) -kernel $(VMLUME) -s -S
-
-# ============================================================
-# Utilities
-# ============================================================
-clean:
- rm -rf $(BUILD)
-
-disasm: $(VMLUME)
- @echo " OBJDUMP $<"
- @$(OBJDUMP) -d -S $(VMLUME) > $(VMLUME).asm
- @echo " Written to $(VMLUME).asm"
-
-fmt:
- @echo " FMT Formatting source files..."
- @find . -name '*.cc' -o -name '*.h' | xargs clang-format -i 2>/dev/null || true
-
-lint:
- @echo " LINT Generating compile_commands.json via bear..."
- @bear -- $(MAKE) all
- @echo " LINT Running clang-tidy on C++ sources..."
- @clang-tidy -p . $(SRCS_CC) || true
-
-# Print build info (for debugging Makefile issues)
-info:
- @echo "TOOLCHAIN_PREFIX = $(TOOLCHAIN_PREFIX)"
- @echo "SRCS_CC = $(SRCS_CC)"
- @echo "SRCS_S = $(SRCS_S)"
- @echo "OBJS = $(OBJS)"
- @echo "KCPPFLAGS = $(KCPPFLAGS)"
diff --git a/na/arch/riscv/config.cc b/na/arch/riscv/config.cc
deleted file mode 100644
index a18a825..0000000
--- a/na/arch/riscv/config.cc
+++ /dev/null
@@ -1,276 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * RISC-V Architecture Configuration — runtime state and KASLR
- *
- * Provides the runtime (post-KASLR) values of the higher-half
- * virtual address bases. kaslr_init() is called once during
- * early boot, before vmm_init(), to randomise the layout and
- * fill the unified KaslrLayout structure.
- *
- * KASLR slide budget:
- * The default Direct Map starts at 0xFFFFFFC0_0000_0000 (VPN[2]=256).
- * The default Vmemmap starts at 0xFFFFFFD0_0000_0000 (VPN[2]=320).
- * A 1GB superpage occupies one VPN[2] slot. We allow a slide of
- * 0..63 slots (0..63 GB) upward, which keeps both regions inside
- * the upper canonical half and avoids colliding with the kernel
- * text region (linked at VPN[2] = 258 + 2 = ~0xFFFFFFC0_8020_0000).
- *
- * In practice, with 128MB RAM the kernel+direct map only touches
- * VPN[2] 258-259, so a slide of up to 63GB is safe.
- */
-
-#include <arch/config.h>
-#include <lume/fdt.h>
-#include <lume/page.h>
-#include <lume/config.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/random.h>
-
-extern "C" char _stext[], _kernel_end[];
-
-namespace arch {
-
-/* Global KASLR Layout Structure*/
-KaslrLayout g_kaslr_layout = {};
-
-/* Runtime bases — updated by kaslr_layout_compute() */
-uint64 g_direct_map_base = kDirectMapBaseDefault;
-uint64 g_vmemmap_base = kVmemmapBaseDefault;
-
-/* VA Range for validation */
-struct VaRange {
- const char* name;
- uint64 start;
- uint64 end; // exclusive
-};
-
-/* ================================================================
- * Utilities
- * ================================================================ */
-static inline uint64 align_down_page(uint64 v)
-{
- return v & ~(kPageSize - 1);
-}
-
-static inline uint64 align_up_page(uint64 v)
-{
- return (v + kPageSize - 1) & ~(kPageSize - 1);
-}
-
-static uint64 checked_add_u64(uint64 a, uint64 b, const char* detail)
-{
- if (a > (~0ULL - b)) {
- kernel_panic("kaslr: address overflow", detail);
- }
- return a + b;
-}
-
-/* Splitmix64: non-plaintext hash derived from seed (used for seed_hash) */
-static uint64 splitmix64(uint64 x)
-{
- x += 0x9e3779b97f4a7c15ULL;
- x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
- x = x ^ (x >> 27);
- return x * 0x94d049bb133111ebULL;
-}
-
-/* SV39 canonical VA check: bits [63:39] must equal sign bit 38. */
-static bool is_sv39_canonical(uint64 va)
-{
- uint64 sign = (va >> 38) & 1ULL;
- uint64 upper = va >> 39;
- return sign ? (upper == ((1ULL << 25) - 1ULL)) : (upper == 0);
-}
-
-static bool is_sv39_kernel_half(uint64 va)
-{
- return is_sv39_canonical(va) && (((va >> 38) & 1ULL) == 1ULL);
-}
-
-static bool ranges_overlap(const VaRange& a, const VaRange& b)
-{
- return (a.start < b.end) && (b.start < a.end);
-}
-
-/* ================================================================
- * KASLR Computation (Phase A: Centralized in single function)
- *
- * This function:
- * 1. Generates or derives random seed
- * 2. Selects 1GB-aligned slot [0..63]
- * 3. Computes seed_hash via splitmix64
- * 4. Fills KaslrLayout structure completely
- * 5. Validates ranges (canonical, overlaps)
- * 6. Logs according to CONFIG_KASLR_LOG_LEVEL
- * ================================================================ */
-static void kaslr_layout_compute()
-{
-#ifdef CONFIG_KASLR
- constexpr uint64 k1GB = 1ULL << 30;
- constexpr uint64 kMaxSlots = 64;
-
- /* 1. Get seed and source */
- uint64 seed = random_early_seed_value();
- RandomSeedSource rsrc = random_early_seed_source();
- KaslrSeedSource source;
- switch (rsrc) {
- case RandomSeedSource::FdtProvided:
- source = KaslrSeedSource::kFdt;
- break;
- case RandomSeedSource::HwRandom:
- source = KaslrSeedSource::kHardware;
- break;
- case RandomSeedSource::CycleFallback:
- case RandomSeedSource::Unknown:
- default:
- source = KaslrSeedSource::kCycle;
- break;
- }
-
- /* 2. Select random slot and compute slide */
- uint64 raw = get_random_u64();
- uint64 slot = raw % kMaxSlots;
- if (slot == 0) {
- slot = 1; /* slot=0 causes direct_map to overlap with kernel image VA */
- }
- uint64 slide = slot * k1GB;
-
- /* 3. Compute non-plaintext hash */
- uint64 seed_hash = splitmix64(seed ^ 0xdeadbeefdeadbeefULL);
-
- /* 4. Compute randomised bases */
- uint64 direct_map_base = kDirectMapBaseDefault + slide;
- uint64 vmemmap_base = kVmemmapBaseDefault + slide;
-
- /* 5. Fill KaslrLayout */
- g_kaslr_layout.seed = seed;
- g_kaslr_layout.seed_hash = seed_hash;
- g_kaslr_layout.source = source;
- g_kaslr_layout.slot = slot;
- g_kaslr_layout.slide_bytes = slide;
- g_kaslr_layout.policy_id = kKaslrPolicyId;
- g_kaslr_layout.direct_map_base = direct_map_base;
- g_kaslr_layout.vmemmap_base = vmemmap_base;
- g_kaslr_layout.canonical_ok = false;
- g_kaslr_layout.overlap_ok = false;
-
- /* Update global runtime bases */
- g_direct_map_base = direct_map_base;
- g_vmemmap_base = vmemmap_base;
-
- /* 6. Self-check: canonical VA validation and overlap detection */
- uint64 mem_base = 0, mem_size = 0;
- fdt_early_get_mem_info(&mem_base, &mem_size);
- if (mem_size == 0) {
- kernel_panic("kaslr_layout_compute: invalid memory from FDT");
- }
-
- /* Construct VA ranges */
- uint64 num_pages = mem_size / kPageSize;
- uint64 page_bytes = num_pages * sizeof(Page);
- uint64 page_span = align_up_page(page_bytes);
-
- VaRange direct_map {
- "direct-map",
- checked_add_u64(g_direct_map_base, mem_base, "direct_map start"),
- checked_add_u64(checked_add_u64(g_direct_map_base, mem_base,
- "direct_map start"),
- mem_size, "direct_map end"),
- };
-
- VaRange vmemmap {
- "vmemmap",
- g_vmemmap_base,
- checked_add_u64(g_vmemmap_base, page_span, "vmemmap end"),
- };
-
- VaRange kernel_img {
- "kernel-image",
- align_down_page(reinterpret_cast<uint64>(_stext)),
- align_up_page(reinterpret_cast<uint64>(_kernel_end)),
- };
-
- /* Validate each range is canonical */
- auto check_kernel_half = [](const VaRange& r) {
- if (r.start >= r.end) {
- kernel_panic("kaslr: invalid VA range", r.name);
- }
- if (!is_sv39_kernel_half(r.start) || !is_sv39_kernel_half(r.end - 1)) {
- kernel_panic("kaslr: non-canonical kernel VA range", r.name);
- }
- };
-
- check_kernel_half(direct_map);
- check_kernel_half(vmemmap);
- check_kernel_half(kernel_img);
-
- /* Validate no overlaps */
- if (ranges_overlap(direct_map, kernel_img)) {
- kernel_panic("kaslr: direct-map overlaps kernel image");
- }
- if (ranges_overlap(vmemmap, kernel_img)) {
- kernel_panic("kaslr: vmemmap overlaps kernel image");
- }
- if (ranges_overlap(direct_map, vmemmap)) {
- kernel_panic("kaslr: direct-map overlaps vmemmap");
- }
-
- /* Mark validation success */
- g_kaslr_layout.canonical_ok = true;
- g_kaslr_layout.overlap_ok = true;
-
- /* 7. Logging according to CONFIG_KASLR_LOG_LEVEL */
- if (kKaslrLogLevel >= 1) {
- /* Minimal logging: just the seed and slot (release mode) */
- kprintf("[boot][kaslr] policy_id=%u source=%s slot=%llu\n",
- g_kaslr_layout.policy_id,
- random_seed_source_name(rsrc),
- slot);
- }
-
- if (kKaslrLogLevel >= 2) {
- /* Debug logging: full ranges and hashes */
- kprintf("[boot][kaslr] seed=0x%llx seed_hash=0x%llx\n",
- seed, seed_hash);
- kprintf("[boot][kaslr] direct-map=[0x%llx,0x%llx)\n",
- direct_map.start, direct_map.end);
- kprintf("[boot][kaslr] vmemmap=[0x%llx,0x%llx)\n",
- vmemmap.start, vmemmap.end);
- kprintf("[boot][kaslr] kernel-image=[0x%llx,0x%llx)\n",
- kernel_img.start, kernel_img.end);
- }
-
-#else /* CONFIG_KASLR disabled */
- g_kaslr_layout.seed = 0;
- g_kaslr_layout.seed_hash = 0;
- g_kaslr_layout.source = KaslrSeedSource::kCycle;
- g_kaslr_layout.slot = 0;
- g_kaslr_layout.slide_bytes = 0;
- g_kaslr_layout.policy_id = kKaslrPolicyId;
- g_kaslr_layout.direct_map_base = kDirectMapBaseDefault;
- g_kaslr_layout.vmemmap_base = kVmemmapBaseDefault;
- g_kaslr_layout.canonical_ok = true;
- g_kaslr_layout.overlap_ok = true;
-
- g_direct_map_base = kDirectMapBaseDefault;
- g_vmemmap_base = kVmemmapBaseDefault;
-
- if (kKaslrLogLevel >= 1) {
- kprintf("[boot][kaslr] disabled policy_id=%u\n",
- g_kaslr_layout.policy_id);
- }
-#endif
-}
-
-/* ================================================================
- * Public entry point
- * ================================================================ */
-void kaslr_init()
-{
- kaslr_layout_compute();
-}
-
-} // namespace arch
diff --git a/na/arch/riscv/defconfig b/na/arch/riscv/defconfig
deleted file mode 100644
index 87d3ffd..0000000
--- a/na/arch/riscv/defconfig
+++ /dev/null
@@ -1,39 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (C) 2026 Richard Qin
-#
-# LumeOS Default Kernel Configuration — RISC-V (virt)
-#
-# This is the "golden" default configuration for the RISC-V virt platform.
-# To customise, copy this file to the source root as '.config':
-#
-# cp arch/riscv/defconfig .config
-# $EDITOR .config
-# make
-#
-# Any variable not set here inherits its default from scripts/Kconfig.mk.
-# ======================================================================
-
-# --- Platform -----------------------------------------------------------
-CONFIG_ARCH = riscv
-
-# --- CPU / SMP ----------------------------------------------------------
-CONFIG_NR_CPUS = 8
-
-# --- Scheduler ----------------------------------------------------------
-CONFIG_HZ = 100
-CONFIG_NR_PRIORITIES = 32
-
-# --- Memory Management --------------------------------------------------
-CONFIG_PAGE_SIZE = 4096
-CONFIG_MAX_ORDER = 11
-CONFIG_KERNEL_STACK_SIZE = 8192
-
-# --- Security / Hardening -----------------------------------------------
-CONFIG_KASLR = y
-CONFIG_STACK_CANARY = y
-
-# --- Debug ---------------------------------------------------------------
-CONFIG_DEBUG = y
-CONFIG_DEBUG_LOCKS = y
-CONFIG_SELFTEST = y
-CONFIG_KLOG = y
diff --git a/na/arch/riscv/include/arch/config.h b/na/arch/riscv/include/arch/config.h
deleted file mode 100644
index 8ae0502..0000000
--- a/na/arch/riscv/include/arch/config.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * RISC-V Architecture Configuration
- *
- * These constants define the architecture-specific memory layout.
- * Each arch/ implementation must provide this header with the same symbols.
- *
- * KASLR: At boot, kaslr_init() randomises g_direct_map_base and
- * g_vmemmap_base. All MI code must read these runtime variables
- * (via pa_to_va / vmm helpers) rather than the static defaults.
- */
-
-#include <lume/types.h>
-
-namespace arch {
-
-/* ================================================================
- * Default (compile-time) virtual addresses
- *
- * SV39 gives a 512GB virtual address space.
- * Higher-half layout (defaults before KASLR):
- *
- * 0xFFFFFFC0_00000000 (256) Direct map (PA+offset)
- * 0xFFFFFFD0_00000000 (320) Vmemmap (Page array)
- *
- * The 64GB gap between them is the KASLR slide range.
- * ================================================================ */
-inline constexpr uint64 kDirectMapBaseDefault = 0xFFFFFFC000000000ULL;
-inline constexpr uint64 kVmemmapBaseDefault = 0xFFFFFFD000000000ULL;
-
-/* Physical memory base (where RAM starts on RISC-V virt platform) */
-inline constexpr uint64 kPhysBase = 0x80000000ULL;
-
-/* Kernel physical load address (= PHYS_BASE in linker script).
- * Everything in [kPhysBase, kPhysStart) is firmware territory (PMP-protected).
- * Analogous to Linux's CONFIG_PHYSICAL_START. */
-inline constexpr uint64 kPhysStart = 0x80200000ULL;
-
-/* ================================================================
- * Runtime (post-KASLR) virtual addresses
- *
- * These start equal to the defaults above. If CONFIG_KASLR is
- * enabled, kaslr_init() adds a random, 1GB-aligned offset
- * to each, sliding both regions upward in VA space.
- * ================================================================ */
-extern uint64 g_direct_map_base;
-extern uint64 g_vmemmap_base;
-
-/* ================================================================
- * KASLR Layout Structure (Phase A: Unified KASLR State)
- *
- * This struct holds all KASLR results: the random seed, derived
- * hash, selected slot, and the randomised virtual address bases.
- * It serves as the single source of truth for all KASLR decisions
- * and is filled once during boot and read-only thereafter.
- * ================================================================ */
-
-enum class KaslrSeedSource : uint8 {
- kFdt = 0, /* Loaded from FDT /random node */
- kHardware = 1, /* From hardware RNG (e.g., RDRAND) */
- kCycle = 2, /* Fallback: CPU cycle counter */
-};
-
-struct KaslrLayout {
- /* Seed and provenance */
- uint64 seed; /* 64-bit random seed value */
- uint64 seed_hash; /* Non-plaintext derived hash (splitmix64) */
- KaslrSeedSource source; /* Seed provenance (fdt/hw/cycle) */
-
- /* Randomisation parameters */
- uint64 slot; /* Slot index: [0, 63] */
- uint64 slide_bytes; /* Byte offset: slot * 1GB */
-
- /* Build-time policy */
- uint32 policy_id; /* CONFIG_KASLR_POLICY_ID at build time */
-
- /* Randomised base addresses (post-KASLR) */
- uint64 direct_map_base; /* Direct map VA (kDirectMapBaseDefault + slide) */
- uint64 vmemmap_base; /* Vmemmap VA (kVmemmapBaseDefault + slide) */
-
- /* Validation flags (for debug logging) */
- bool canonical_ok; /* All VA ranges passed canonical check */
- bool overlap_ok; /* No overlaps detected between regions */
-};
-
-/* ================================================================
- * KASLR entry point
- *
- * Must be called AFTER random_early_init() and BEFORE vmm_init().
- * Fills g_kaslr_layout with randomised bases and self-check results.
- * When CONFIG_KASLR is disabled this is a no-op.
- * ================================================================ */
-extern KaslrLayout g_kaslr_layout;
-
-void kaslr_init();
-
-/* Helper to query the KASLR layout safely */
-static inline const KaslrLayout& kaslr_layout_get() {
- return g_kaslr_layout;
-}
-
-} // namespace arch
diff --git a/na/arch/riscv/include/arch/context.h b/na/arch/riscv/include/arch/context.h
deleted file mode 100644
index 3d935fa..0000000
--- a/na/arch/riscv/include/arch/context.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Context Switch Structure (RISC-V 64)
- *
- * Holds only callee-saved registers for voluntary (non-preemptive)
- * context switches between kernel threads / tasks.
- *
- * Used by: arch::context_switch() in swtch.S
- * Reference: docs/specs/arch_abstraction.md §3.1
- */
-
-#include <lume/types.h>
-
-namespace arch {
-
-struct Context {
- uint64 ra; // 0: Return address
- uint64 sp; // 8: Stack pointer
- uint64 s[12]; // 16: s0-s11 (callee-saved registers)
-};
-
-static_assert(sizeof(Context) == 112);
-
-// Assembly-implemented context switch.
-// Saves current CPU registers to old_ctx, loads next_ctx into CPU.
-// Defined in arch/riscv/trap/swtch.S
-extern "C" void context_switch(Context* old_ctx, Context* next_ctx);
-
-} // namespace arch
diff --git a/na/arch/riscv/include/arch/cpu.h b/na/arch/riscv/include/arch/cpu.h
deleted file mode 100644
index 8c573ec..0000000
--- a/na/arch/riscv/include/arch/cpu.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * CPU Control & Status Primitives (RISC-V 64)
- *
- * Provides interrupt enable/disable, CPU ID, timer, and halt.
- * All functions are static inline — zero call overhead.
- *
- * Precondition: tp register must be initialized with CPU ID
- * by entry.S before any of these functions are called.
- *
- * Reference: docs/specs/arch_abstraction.md §4.1
- */
-
-#include <lume/types.h>
-
-/* RISC-V sstatus register bit definitions */
-#define SSTATUS_SIE (1UL << 1) /* Supervisor Interrupt Enable */
-#define SSTATUS_SPIE (1UL << 5) /* Supervisor Previous Interrupt Enable */
-#define SSTATUS_SPP (1UL << 8) /* Supervisor Previous Privilege (0=U, 1=S) */
-
-/* RISC-V sie register bit definitions */
-#define SIE_SSIE (1UL << 1) /* Supervisor Software Interrupt Enable */
-#define SIE_STIE (1UL << 5) /* Supervisor Timer Interrupt Enable */
-#define SIE_SEIE (1UL << 9) /* Supervisor External Interrupt Enable */
-
-namespace arch::cpu {
-
-// Get current CPU ID (from tp register, set by entry.S)
-static inline uint64 id() {
- uint64 val;
- __asm__ volatile("mv %0, tp" : "=r"(val));
- return val;
-}
-
-// Enable interrupts (set sstatus.SIE = 1)
-static inline void intr_on() {
- __asm__ volatile("csrs sstatus, %0" :: "r"(SSTATUS_SIE));
-}
-
-// Disable interrupts (clear sstatus.SIE = 0)
-static inline void intr_off() {
- __asm__ volatile("csrc sstatus, %0" :: "r"(SSTATUS_SIE));
-}
-
-// Check if interrupts are currently enabled
-static inline bool intr_enabled() {
- uint64 sstatus;
- __asm__ volatile("csrr %0, sstatus" : "=r"(sstatus));
- return (sstatus & SSTATUS_SIE) != 0;
-}
-
-// Halt CPU until next interrupt (wfi instruction)
-static inline void halt_until_interrupt() {
- __asm__ volatile("wfi");
-}
-
-// Read hardware monotonic timer (rdtime instruction)
-// Returns ticks since boot; frequency from FDT timebase-frequency.
-static inline uint64 read_time() {
- uint64 val;
- __asm__ volatile("rdtime %0" : "=r"(val));
- return val;
-}
-
-// Enable timer interrupt in sie (sie.STIE = 1)
-static inline void timer_intr_on() {
- __asm__ volatile("csrs sie, %0" :: "r"(SIE_STIE));
-}
-
-// Disable timer interrupt in sie (sie.STIE = 0)
-static inline void timer_intr_off() {
- __asm__ volatile("csrc sie, %0" :: "r"(SIE_STIE));
-}
-
-// Enable software interrupt in sie (sie.SSIE = 1) — needed for IPI
-static inline void software_intr_on() {
- __asm__ volatile("csrs sie, %0" :: "r"(SIE_SSIE));
-}
-
-// Enable external interrupt in sie (sie.SEIE = 1) — needed for PLIC (UART, etc.)
-static inline void external_intr_on() {
- __asm__ volatile("csrs sie, %0" :: "r"(SIE_SEIE));
-}
-
-// Clear S-mode software interrupt pending bit (sip.SSIP = 0)
-// Must be called in the IPI handler to acknowledge the interrupt.
-static inline void clear_sip_ssip() {
- __asm__ volatile("csrc sip, %0" :: "r"(1UL << 1));
-}
-
-} // namespace arch::cpu
diff --git a/na/arch/riscv/include/arch/extable.h b/na/arch/riscv/include/arch/extable.h
deleted file mode 100644
index eb2daac..0000000
--- a/na/arch/riscv/include/arch/extable.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-#ifndef __ASSEMBLER__
-
-#include <lume/types.h>
-
-/*
- * Exception table entry.
- * insn: offset from this struct to the faulting instruction.
- * fixup: offset from this struct to the fixup code.
- *
- * Using relative offsets avoids absolute relocations and saves space.
- */
-struct ExceptionTableEntry {
- int32 insn;
- int32 fixup;
-};
-
-/* Search the exception table for a given PC. Returns fixup address, or 0 if not found. */
-uint64 search_extable(uint64 pc);
-
-/* Sorts the exception table at boot for binary search O(log N). */
-void extable_init();
-
-#endif /* __ASSEMBLER__ */
-
-/*
- * EXTABLE macro for assembly usage.
- * Appends an entry to the .extable section.
- */
-#ifdef __ASSEMBLER__
-
-#define EXTABLE(insn, fixup) \
- .pushsection .extable, "a"; \
- .balign 4; \
- .long insn - .; \
- .long fixup - .; \
- .popsection
-
-#else /* !__ASSEMBLER__ */
-
-/* Stringified macro for inline assembly */
-#define _ASM_EXTABLE(insn, fixup) \
- ".pushsection .extable, \"a\"\n" \
- ".balign 4\n" \
- ".long " #insn " - .\n" \
- ".long " #fixup " - .\n" \
- ".popsection\n"
-
-#define EXTABLE(insn, fixup) _ASM_EXTABLE(insn, fixup)
-
-#endif /* __ASSEMBLER__ */
diff --git a/na/arch/riscv/include/arch/fpstate.h b/na/arch/riscv/include/arch/fpstate.h
deleted file mode 100644
index 4b42a22..0000000
--- a/na/arch/riscv/include/arch/fpstate.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * FPU State — Floating-Point Register Context
- *
- * Pre-reserved structure for RISC-V F/D extension floating-point state.
- * MUST be allocated in every TaskControlBlock so that future FPU
- * save/restore logic has a stable destination without restructuring TCB.
- *
- * RISC-V F/D extension state:
- * - 32 FP registers (f0-f31), each 64 bits for D extension
- * - fcsr (Floating-point Control and Status Register)
- *
- * sstatus.FS field management:
- * - FS=0 (Off): FPU disabled, any FP instruction traps
- * - FS=1 (Initial): FPU enabled, registers are initial state
- * - FS=2 (Clean): FPU enabled, registers not modified since last save
- * - FS=3 (Dirty): FPU enabled, registers modified (need save)
- *
- * Current status: Structure defined, save/restore NOT implemented.
- * When implemented, trap_dispatch and swtch.S will use lazy
- * save/restore based on sstatus.FS dirty tracking.
- *
- * Reference: RISC-V ISA Manual §3.1.6.5 (FS field)
- */
-
-#include <lume/types.h>
-
-namespace arch {
-
-struct FpState {
- uint64 f[32]; // f0-f31 (64-bit each for D extension)
- uint32 fcsr; // Floating-point CSR (frm + fflags)
- uint32 _pad; // Alignment padding
-};
-
-static_assert(sizeof(FpState) == 32 * 8 + 8, "FpState must be 264 bytes");
-
-/*
- * sstatus.FS field constants
- */
-inline constexpr uint64 kSstatusFS_Off = 0ULL << 13;
-inline constexpr uint64 kSstatusFS_Initial = 1ULL << 13;
-inline constexpr uint64 kSstatusFS_Clean = 2ULL << 13;
-inline constexpr uint64 kSstatusFS_Dirty = 3ULL << 13;
-inline constexpr uint64 kSstatusFS_Mask = 3ULL << 13;
-
-/*
- * Check if sstatus indicates FPU state is dirty (needs save).
- */
-inline bool fpu_state_dirty(uint64 sstatus) {
- return (sstatus & kSstatusFS_Mask) == kSstatusFS_Dirty;
-}
-
-/*
- * Check if FPU is enabled (FS != Off).
- */
-inline bool fpu_enabled(uint64 sstatus) {
- return (sstatus & kSstatusFS_Mask) != kSstatusFS_Off;
-}
-
-} // namespace arch
-
-/* Assembly routines for FPU register save/restore (fpu.S) */
-extern "C" void fpu_save(arch::FpState* state);
-extern "C" void fpu_restore(arch::FpState* state);
-
-namespace arch {
-
-/*
- * Read sstatus.FS field (current FPU state)
- */
-inline uint64 fpu_get_fs() {
- uint64 sstatus;
- __asm__ volatile("csrr %0, sstatus" : "=r"(sstatus));
- return sstatus & kSstatusFS_Mask;
-}
-
-/*
- * Set sstatus.FS field.
- * @fs: one of kSstatusFS_Off, kSstatusFS_Initial, kSstatusFS_Clean, kSstatusFS_Dirty
- */
-inline void fpu_set_fs(uint64 fs) {
- __asm__ volatile(
- "csrc sstatus, %0\n"
- "csrs sstatus, %1\n"
- :: "r"(kSstatusFS_Mask), "r"(fs)
- );
-}
-
-} // namespace arch
diff --git a/na/arch/riscv/include/arch/mmu.h b/na/arch/riscv/include/arch/mmu.h
deleted file mode 100644
index f4c9b26..0000000
--- a/na/arch/riscv/include/arch/mmu.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * MMU Hardware Primitives (RISC-V SV39)
- *
- * Page table switching, TLB flushing, and fault address reading.
- * All functions are static inline — zero call overhead.
- *
- * Note on SMP: flush_tlb_page() only flushes the LOCAL CPU's TLB.
- * Cross-CPU TLB shootdown via IPI is not yet implemented.
- *
- * Reference: docs/specs/arch_abstraction.md §4.2
- */
-
-#include <lume/types.h>
-
-/* SATP register: mode field for SV39 */
-#define SATP_SV39 (8UL << 60)
-
-/* SATP PPN mask (bits 43:0) */
-#define SATP_PPN_MASK ((1UL << 44) - 1)
-
-namespace arch::mmu {
-
-// Read the faulting virtual address from stval (set by HW on page fault)
-static inline uint64 get_fault_address() {
- uint64 val;
- __asm__ volatile("csrr %0, stval" : "=r"(val));
- return val;
-}
-
-// Switch the top-level page table.
-// Takes a PHYSICAL ADDRESS (not PPN); internally shifts right by 12
-// and combines with SV39 mode bits before writing to satp.
-static inline void set_page_table(uint64 physical_pgdir_addr) {
- uint64 ppn = physical_pgdir_addr >> 12;
- uint64 satp_val = SATP_SV39 | ppn;
- __asm__ volatile("csrw satp, %0" :: "r"(satp_val));
- // Note: caller should call flush_tlb_all() after this
-}
-
-// Read the current page table physical address.
-// Extracts PPN from satp and converts back to physical address.
-static inline uint64 get_page_table() {
- uint64 satp_val;
- __asm__ volatile("csrr %0, satp" : "=r"(satp_val));
- return (satp_val & SATP_PPN_MASK) << 12;
-}
-
-// Read the raw satp CSR value (mode + PPN).
-// Used when the caller needs the exact register value (e.g. for TrapFrame metadata).
-static inline uint64 read_satp() {
- uint64 val;
- __asm__ volatile("csrr %0, satp" : "=r"(val));
- return val;
-}
-
-// Flush the entire TLB (all ASIDs, all addresses)
-static inline void flush_tlb_all() {
- __asm__ volatile("sfence.vma zero, zero" ::: "memory");
-}
-
-// Flush a single TLB entry for the given virtual address
-static inline void flush_tlb_page(uint64 vaddr) {
- __asm__ volatile("sfence.vma %0, zero" :: "r"(vaddr) : "memory");
-}
-
-// Flush the instruction cache.
-// Required after loading executable code into memory (e.g. ELF loading).
-// sfence.vma only flushes the TLB; fence.i is needed for the I-cache.
-static inline void flush_icache() {
- __asm__ volatile("fence.i" ::: "memory");
-}
-
-} // namespace arch::mmu
diff --git a/na/arch/riscv/include/arch/pmap.h b/na/arch/riscv/include/arch/pmap.h
deleted file mode 100644
index 0b43b9e..0000000
--- a/na/arch/riscv/include/arch/pmap.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * pmap — SV39 page table interface (MD layer)
- *
- * Namespace functions, zero runtime overhead.
- * Implementation: arch/riscv/mm/pmap.cc
- *
- * Reference: docs/specs/vmm.md §3.2
- */
-
-#include <lume/types.h>
-
-namespace pmap {
-
-/* PTE permission bits */
-inline constexpr uint64 PTE_V = 1ULL << 0;
-inline constexpr uint64 PTE_R = 1ULL << 1;
-inline constexpr uint64 PTE_W = 1ULL << 2;
-inline constexpr uint64 PTE_X = 1ULL << 3;
-inline constexpr uint64 PTE_U = 1ULL << 4;
-inline constexpr uint64 PTE_G = 1ULL << 5;
-inline constexpr uint64 PTE_A = 1ULL << 6;
-inline constexpr uint64 PTE_D = 1ULL << 7;
-inline constexpr uint64 PTE_COW = 1ULL << 8; /* Custom RSW bit for Copy-On-Write */
-
-uint64 create();
-void destroy(uint64 root_pa);
-int map(uint64 root_pa, uint64 va, uint64 pa, uint64 perm);
-int map_2mb(uint64 root_pa, uint64 va, uint64 pa, uint64 perm);
-void unmap(uint64 root_pa, uint64 va);
-int protect(uint64 root_pa, uint64 va, uint64 perm);
-int protect_range(uint64 root_pa, uint64 start, uint64 end, uint64 perm);
-bool lookup(uint64 root_pa, uint64 va, uint64* pa_out);
-uint64 lookup_pte(uint64 root_pa, uint64 va);
-bool is_cow(uint64 root_pa, uint64 va);
-int make_cow(uint64 root_pa, uint64 va);
-void activate(uint64 root_pa);
-
-/*
- * Translate MI-layer VM_* permission flags to hardware PTE bits.
- * Called by vma.cc when faulting in a page.
- */
-uint64 vm_perm_to_pte(uint64 vm_perm);
-
-} // namespace pmap
diff --git a/na/arch/riscv/include/arch/random.h b/na/arch/riscv/include/arch/random.h
deleted file mode 100644
index 8c8c051..0000000
--- a/na/arch/riscv/include/arch/random.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-#include <lume/types.h>
-
-namespace arch {
-
-/*
- * Get a random 64-bit value from the hardware TRNG.
- * Returns true if supported and successful, false otherwise.
- */
-bool get_hw_random(uint64& value);
-
-/*
- * Get the current high-resolution hardware cycle counter.
- * Used as an entropy fallback (Jitter/Timer).
- */
-inline uint64 get_cycles()
-{
- uint64 cycles;
- __asm__ volatile("rdcycle %0" : "=r"(cycles));
- return cycles;
-}
-
-} // namespace arch
diff --git a/na/arch/riscv/include/arch/sbi.h b/na/arch/riscv/include/arch/sbi.h
deleted file mode 100644
index 80a6d7c..0000000
--- a/na/arch/riscv/include/arch/sbi.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * sbi.h — RISC-V Supervisor Binary Interface (SBI) wrappers
- *
- * Provides inline ecall wrappers for SBI calls from S-mode.
- * Uses the SBI v1.0+ extension-based calling convention.
- *
- * Reference: RISC-V SBI Specification v2.0
- */
-
-#include <lume/types.h>
-
-namespace arch::sbi {
-
-struct sbiret {
- long error;
- long value;
-};
-
-/* SBI error codes */
-inline constexpr long SBI_SUCCESS = 0;
-inline constexpr long SBI_ERR_NOT_SUPPORTED = -2;
-
-/* Extension IDs */
-inline constexpr int SBI_EXT_TIME = 0x54494D45; /* "TIME" */
-inline constexpr int SBI_EXT_IPI = 0x735049; /* "sPI" */
-inline constexpr int SBI_EXT_RFENCE = 0x52464E43; /* "RFNC" */
-inline constexpr int SBI_EXT_HSM = 0x48534D; /* "HSM" */
-
-static inline sbiret sbi_ecall(int ext, int fid, uint64 arg0 = 0,
- uint64 arg1 = 0, uint64 arg2 = 0,
- uint64 arg3 = 0, uint64 arg4 = 0,
- uint64 arg5 = 0) {
- sbiret ret;
- register uint64 a0 asm("a0") = arg0;
- register uint64 a1 asm("a1") = arg1;
- register uint64 a2 asm("a2") = arg2;
- register uint64 a3 asm("a3") = arg3;
- register uint64 a4 asm("a4") = arg4;
- register uint64 a5 asm("a5") = arg5;
- register uint64 a6 asm("a6") = fid;
- register uint64 a7 asm("a7") = ext;
- __asm__ volatile("ecall"
- : "+r"(a0), "+r"(a1)
- : "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6), "r"(a7)
- : "memory");
- ret.error = static_cast<long>(a0);
- ret.value = static_cast<long>(a1);
- return ret;
-}
-
-/* ---- Timer Extension (EID 0x54494D45) ---- */
-
-static inline void set_timer(uint64 stime_value) {
- sbi_ecall(SBI_EXT_TIME, 0, stime_value);
-}
-
-/* ---- IPI Extension (EID 0x735049) ---- */
-
-/*
- * sbi_send_ipi — Send S-mode software interrupt to target harts
- *
- * @hart_mask: bitmask of target harts (relative to hart_mask_base)
- * @hart_mask_base: base hart ID for the mask (0 = harts 0-63)
- */
-static inline sbiret sbi_send_ipi(uint64 hart_mask,
- uint64 hart_mask_base = 0) {
- return sbi_ecall(SBI_EXT_IPI, 0, hart_mask, hart_mask_base);
-}
-
-/* ---- Remote Fence Extension (EID 0x52464E43) ---- */
-
-/*
- * sbi_remote_sfence_vma — Execute SFENCE.VMA on target harts
- *
- * @hart_mask/base: target harts
- * @start_addr: start of VA range to invalidate
- * @size: size of range (use ~0UL for full flush)
- */
-static inline sbiret sbi_remote_sfence_vma(uint64 hart_mask,
- uint64 hart_mask_base,
- uint64 start_addr,
- uint64 size) {
- return sbi_ecall(SBI_EXT_RFENCE, 1,
- hart_mask, hart_mask_base,
- start_addr, size);
-}
-
-/* ---- HSM Extension (EID 0x48534D) ---- */
-
-/*
- * sbi_hart_start — Start (wake) a hart that is in STOPPED state
- *
- * @hartid: target hart ID to start
- * @start_addr: PHYSICAL address where the hart should begin execution
- * @opaque: passed in a1 register to the hart (e.g. FDT pointer)
- *
- * OpenSBI v1.0+ parks non-boot harts; they must be explicitly started.
- */
-static inline sbiret sbi_hart_start(uint64 hartid,
- uint64 start_addr,
- uint64 opaque) {
- return sbi_ecall(SBI_EXT_HSM, 0, hartid, start_addr, opaque);
-}
-
-} // namespace arch::sbi
-
diff --git a/na/arch/riscv/include/arch/tlb.h b/na/arch/riscv/include/arch/tlb.h
deleted file mode 100644
index 46c6b97..0000000
--- a/na/arch/riscv/include/arch/tlb.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-#include <lume/types.h>
-
-namespace arch::tlb {
-
-/*
- * Perform a cross-CPU TLB flush (shootdown).
- *
- * @param cpu_mask: Bitmask of CPUs to target.
- * @param start: Start virtual address, or 0 for full address space.
- * @param size: Size in bytes, or ~0ULL for full address space.
- */
-void flush_remote(uint64 cpu_mask, uint64 start, uint64 size);
-
-} // namespace arch::tlb
diff --git a/na/arch/riscv/include/arch/trap.h b/na/arch/riscv/include/arch/trap.h
deleted file mode 100644
index 8e55250..0000000
--- a/na/arch/riscv/include/arch/trap.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Trap Vector Control Primitives (RISC-V 64)
- *
- * Set/read the S-mode trap vector (stvec), trap cause (scause),
- * and trap value (stval) registers.
- *
- * Reference: docs/specs/arch_abstraction.md §4.3
- */
-
-#include <lume/types.h>
-
-/* scause interrupt bit (bit 63): 1 = interrupt, 0 = exception */
-#define SCAUSE_INTERRUPT (1UL << 63)
-
-/* Exception cause codes (scause with bit 63 = 0) */
-#define EXC_INST_MISALIGNED 0
-#define EXC_INST_ACCESS_FAULT 1
-#define EXC_ILLEGAL_INST 2
-#define EXC_BREAKPOINT 3
-#define EXC_LOAD_MISALIGNED 4
-#define EXC_LOAD_ACCESS_FAULT 5
-#define EXC_STORE_MISALIGNED 6
-#define EXC_STORE_ACCESS_FAULT 7
-#define EXC_ECALL_FROM_U 8
-#define EXC_ECALL_FROM_S 9
-#define EXC_INST_PAGE_FAULT 12
-#define EXC_LOAD_PAGE_FAULT 13
-#define EXC_STORE_PAGE_FAULT 15
-
-/* Interrupt cause codes (scause with bit 63 = 1, lower bits) */
-#define IRQ_S_SOFTWARE 1
-#define IRQ_S_TIMER 5
-#define IRQ_S_EXTERNAL 9
-
-namespace arch::trap {
-
-// Set S-mode trap vector base address (writes stvec).
-// Mode: Direct (all traps go to handler_addr).
-static inline void set_vector(uint64 handler_addr) {
- // Lowest 2 bits of stvec = mode. 0 = Direct.
- __asm__ volatile("csrw stvec, %0" :: "r"(handler_addr));
-}
-
-// Read current trap vector base address
-static inline uint64 get_vector() {
- uint64 val;
- __asm__ volatile("csrr %0, stvec" : "=r"(val));
- return val;
-}
-
-// Read trap cause register (scause)
-// Bit 63: 1=interrupt, 0=exception. Lower bits: cause code.
-static inline uint64 get_cause() {
- uint64 val;
- __asm__ volatile("csrr %0, scause" : "=r"(val));
- return val;
-}
-
-// Read trap value register (stval)
-// Contains faulting address (page faults) or faulting instruction.
-static inline uint64 get_value() {
- uint64 val;
- __asm__ volatile("csrr %0, stval" : "=r"(val));
- return val;
-}
-
-// Check if a scause value represents an interrupt (vs exception)
-static inline bool is_interrupt(uint64 cause) {
- return (cause & SCAUSE_INTERRUPT) != 0;
-}
-
-// Extract the cause code (strip the interrupt bit)
-static inline uint64 cause_code(uint64 cause) {
- return cause & ~SCAUSE_INTERRUPT;
-}
-
-// Enable Supervisor User Memory access (set SUM bit in sstatus).
-// When SUM=1, S-mode can read/write pages marked PTE_U.
-// This is needed for syscall handlers to access user-space buffers.
-static inline void enable_sum() {
- __asm__ volatile("csrs sstatus, %0" :: "r"(1UL << 18));
-}
-
-// Disable Supervisor User Memory access (clear SUM bit in sstatus).
-static inline void disable_sum() {
- __asm__ volatile("csrc sstatus, %0" :: "r"(1UL << 18));
-}
-
-} // namespace arch::trap
diff --git a/na/arch/riscv/include/arch/trapframe.h b/na/arch/riscv/include/arch/trapframe.h
deleted file mode 100644
index b198646..0000000
--- a/na/arch/riscv/include/arch/trapframe.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Trap Frame Structure (RISC-V 64)
- *
- * Saved/restored by the assembly trap_vector on every interrupt,
- * exception, or system call. Contains the full CPU snapshot needed
- * to resume execution after trap handling.
- *
- * Layout must match the assembly offsets in trap_vector.S exactly.
- * Reference: docs/specs/arch_abstraction.md §3.2
- */
-
-/*
- * TrapFrame field offsets for use in assembly.
- * These must be kept in sync with the struct layout below.
- * Placed before any C++ code so assembly can #include this file.
- */
-#define TF_KERNEL_SATP 0
-#define TF_KERNEL_SP 8
-#define TF_KERNEL_TRAP 16
-#define TF_KERNEL_CPUID 24
-#define TF_EPC 32
-#define TF_SSTATUS 40
-#define TF_RA 48
-#define TF_SP 56
-#define TF_GP 64
-#define TF_TP 72
-#define TF_T0 80
-#define TF_T1 88
-#define TF_T2 96
-#define TF_T3 104
-#define TF_T4 112
-#define TF_T5 120
-#define TF_T6 128
-#define TF_S0 136
-#define TF_S1 144
-#define TF_S2 152
-#define TF_S3 160
-#define TF_S4 168
-#define TF_S5 176
-#define TF_S6 184
-#define TF_S7 192
-#define TF_S8 200
-#define TF_S9 208
-#define TF_S10 216
-#define TF_S11 224
-#define TF_A0 232
-#define TF_A1 240
-#define TF_A2 248
-#define TF_A3 256
-#define TF_A4 264
-#define TF_A5 272
-#define TF_A6 280
-#define TF_A7 288
-#define TF_SIZE 296
-
-#ifndef __ASSEMBLER__
-
-#include <lume/types.h>
-#include <arch/cpu.h>
-
-namespace arch {
-
-struct TrapFrame {
- /* ---- Kernel metadata (offset 0-39) ---- */
- uint64 kernel_satp; // 0: Kernel page table (written to satp on trap entry)
- uint64 kernel_sp; // 8: Top of kernel stack for this task
- uint64 kernel_trap; // 16: Address of C++ trap_handler function
- uint64 kernel_cpu_id; // 24: CPU ID (loaded into tp on trap entry)
- uint64 epc; // 32: Interrupted instruction address (sepc)
-
- /* ---- CSR backup (offset 40-47) ---- */
- uint64 sstatus; // 40: Privilege status (SPP, SPIE bits)
-
- /* ---- General-purpose registers (offset 48-295) ---- */
- uint64 ra; // 48: x1 - Return address
- uint64 sp; // 56: x2 - User stack pointer
- uint64 gp; // 64: x3 - Global pointer
- uint64 tp; // 72: x4 - Thread pointer
- uint64 t[7]; // 80: x5-x7, x28-x31 (t0-t6, caller-saved temporaries)
- uint64 s[12]; // 136: x8-x9, x18-x27 (s0-s11, callee-saved)
- uint64 a[8]; // 232: x10-x17 (a0-a7, arguments / syscall params)
-};
-
-static_assert(sizeof(TrapFrame) == 296, "TrapFrame size mismatch with assembly offsets");
-
-} // namespace arch
-
-/* Forward declaration — defined in trap_md.cc.
- * Must be after TrapFrame definition. */
-extern "C" [[noreturn]] void trap_dispatch(arch::TrapFrame*);
-
-namespace arch {
-
-/*
- * trapframe_init_user — prepare a TrapFrame for returning to user mode.
- *
- * Sets arch-specific sstatus:
- * SPP = 0 (return to U-mode on sret)
- * SPIE = 1 (enable interrupts in U-mode)
- * FS = Initial (allow FP instructions)
- *
- * Called from MI exec.cc instead of setting sstatus directly.
- */
-static inline void trapframe_init_user(TrapFrame* tf, uint64 entry, uint64 sp)
-{
- /* Clear all registers */
- for (auto& t : tf->t) { t = 0; }
- for (auto& s : tf->s) { s = 0; }
- for (auto& a : tf->a) { a = 0; }
- tf->ra = 0;
- tf->gp = 0;
- tf->tp = 0;
-
- tf->epc = entry;
- tf->sp = sp;
-
- /* sstatus: SPP=0 (U-mode), SPIE=1 (interrupts), FS=Initial (FP ok) */
- constexpr uint64 kSPIE = (1ULL << 5);
- constexpr uint64 kFS_Initial = (1ULL << 13);
- tf->sstatus = kSPIE | kFS_Initial;
-
- /* Kernel metadata used by trap_vector.S on every trap from U-mode.
- * These are RISC-V specific — trap_vector reads them from the
- * TrapFrame to switch to kernel context. */
- uint64 satp;
- asm volatile("csrr %0, satp" : "=r"(satp));
- tf->kernel_satp = satp;
- tf->kernel_sp = reinterpret_cast<uint64>(tf);
- tf->kernel_trap = reinterpret_cast<uint64>(&trap_dispatch);
- tf->kernel_cpu_id = cpu::id();
-}
-
-} // namespace arch
-
-#endif /* __ASSEMBLER__ */
diff --git a/na/arch/riscv/include/asm/csr.h b/na/arch/riscv/include/asm/csr.h
deleted file mode 100644
index 42f1eb4..0000000
--- a/na/arch/riscv/include/asm/csr.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _ASM_RISCV_CSR_H
-#define _ASM_RISCV_CSR_H
-
-/*
- * RISC-V CSR (Control & Status Register) definitions used by the early
- * boot / MMU-switch path.
- *
- * LumeOS-native subset of Linux arch/riscv/include/asm/csr.h. Only the
- * symbols head.S and mm/init.cc actually reference are defined — adding
- * the rest is straightforward when new code needs them, and skipping the
- * Linux ALTERNATIVE / vendor-extension machinery keeps the cascade flat.
- */
-
-/* SATP (Supervisor Address Translation and Protection) layout — RV64 */
-#define SATP_PPN_MASK 0x00000FFFFFFFFFFFUL /* bits [43:0] = root PPN */
-#define SATP_ASID_SHIFT 44
-#define SATP_MODE_SHIFT 60
-
-/* SATP MODE encodings (RV64) */
-#define SATP_MODE_BARE (0UL << SATP_MODE_SHIFT)
-#define SATP_MODE_SV39 (8UL << SATP_MODE_SHIFT)
-#define SATP_MODE_SV48 (9UL << SATP_MODE_SHIFT)
-
-/* Compose a SATP value from a page-table physical address.
- * Result: MODE | (root_ppn) where root_ppn = pgd_pa >> PAGE_SHIFT. */
-#define MAKE_SATP(mode, pgd_pa) ((mode) | (((pgd_pa) >> 12) & SATP_PPN_MASK))
-
-/* SSTATUS bits we touch at boot — disable FP / vector at entry so that an
- * accidental FP / V instruction in early code traps cleanly rather than
- * silently using dirty state from before reset. */
-#define SR_FS_OFF (0UL << 13)
-#define SR_VS_OFF (0UL << 9)
-
-#endif /* _ASM_RISCV_CSR_H */
diff --git a/na/arch/riscv/include/asm/page.h b/na/arch/riscv/include/asm/page.h
deleted file mode 100644
index 7b9f155..0000000
--- a/na/arch/riscv/include/asm/page.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _ASM_RISCV_PAGE_H
-#define _ASM_RISCV_PAGE_H
-
-/*
- * RISC-V page / virtual-memory layout constants.
- *
- * Mirrors the subset of Linux arch/riscv/include/asm/page.h that LumeOS's
- * early boot path needs:
- * - PAGE_SIZE / PAGE_SHIFT / PAGE_MASK
- * - PAGE_OFFSET (start of the higher-half direct map)
- * - KERNEL_LINK_ADDR (link-time VA of _start)
- * - SV39 PTE flag bits
- * - PTRS_PER_PGD (512 for SV39)
- *
- * Values match Linux RISC-V SV39 defaults so that struct kernel_mapping
- * (filled by setup_vm in arch/riscv/mm/init.cc) carries the same numeric
- * relations Linux's mm/init.c maintains.
- */
-
-#define PAGE_SHIFT 12
-#define PAGE_SIZE (1UL << PAGE_SHIFT)
-#define PAGE_MASK (~(PAGE_SIZE - 1))
-
-/* Higher-half base of the kernel direct map. Bottom of the SV39 supervisor
- * address space minus the unmapped guard region. Identical to Linux's
- * CONFIG_PAGE_OFFSET default on RV64. */
-#define PAGE_OFFSET 0xFFFFFFC000000000UL
-
-/* Where _start was linked. OpenSBI hands control to PHYS_BASE; the kernel
- * runs at KERNEL_LINK_ADDR after relocate_enable_mmu jumps to higher-half. */
-#define PHYS_BASE 0x80200000UL
-#define KERNEL_LINK_ADDR 0xFFFFFFC080200000UL
-
-/* SATP mode field — SV39 = 8, shifted to bits [63:60]. */
-#define SATP_MODE_SV39 (8UL << 60)
-
-#define PTRS_PER_PGD 512
-
-/* 1 GB superpage size — entry.S used to build the early page table with
- * these; setup_vm in arch/riscv/mm/init.cc now does the same. */
-#define PGDIR_SHIFT 30
-#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
-#define PGDIR_MASK (~(PGDIR_SIZE - 1))
-
-/* SV39 PTE flag bits (one byte). */
-#define _PAGE_PRESENT (1UL << 0) /* aka VALID — historic Linux name */
-#define _PAGE_READ (1UL << 1)
-#define _PAGE_WRITE (1UL << 2)
-#define _PAGE_EXEC (1UL << 3)
-#define _PAGE_USER (1UL << 4)
-#define _PAGE_GLOBAL (1UL << 5)
-#define _PAGE_ACCESSED (1UL << 6)
-#define _PAGE_DIRTY (1UL << 7)
-
-#define PAGE_KERNEL_EXEC \
- (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | _PAGE_EXEC | \
- _PAGE_ACCESSED | _PAGE_DIRTY)
-
-#endif /* _ASM_RISCV_PAGE_H */
diff --git a/na/arch/riscv/include/asm/pgtable.h b/na/arch/riscv/include/asm/pgtable.h
deleted file mode 100644
index 28fc4c5..0000000
--- a/na/arch/riscv/include/asm/pgtable.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _ASM_RISCV_PGTABLE_H
-#define _ASM_RISCV_PGTABLE_H
-
-/*
- * Early page-table construction API used by arch/riscv/mm/init.cc::setup_vm.
- *
- * Mirrors the create_pgd_mapping interface from Linux's
- * arch/riscv/mm/init.c — same name, same semantics:
- *
- * create_pgd_mapping(pgdir, va, pa, sz, prot)
- *
- * pgdir : root of the page table to populate
- * va : virtual address to map
- * pa : physical address to map to
- * sz : mapping size — must be a supported leaf size (PAGE_SIZE,
- * 2 MiB megapage, or 1 GiB gigapage)
- * prot : PTE flag bits (PAGE_KERNEL_EXEC etc., from <asm/page.h>)
- *
- * The boot-time variant in this file only supports gigapage (1 GiB)
- * mappings — that is all setup_vm needs for the early_pg_dir / trampoline_pg_dir
- * built before the slab allocator exists. Finer-grained mappings come
- * later via vmm_init().
- */
-
-#include <lume/types.h>
-#include <asm/page.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct { unsigned long pgd; } pgd_t;
-
-/* Linker-provided early page-table storage (defined in lumeos.lds.S
- * ARCH_BSS_EXTRA, lives in .bss so it's zero-initialized). Allocating
- * them in the linker script — not in C — mirrors Linux and keeps the
- * symbols visible from head.S without a forward C decl mismatch. */
-extern pgd_t early_pg_dir[PTRS_PER_PGD];
-extern pgd_t trampoline_pg_dir[PTRS_PER_PGD];
-extern pgd_t fixmap_pg_dir[PTRS_PER_PGD];
-
-/* Install a single PGD entry mapping `va` → `pa` with size `sz` and PTE
- * flags `prot`. Only sz == PGDIR_SIZE (1 GiB) is supported in this early
- * variant — vmm_init() takes over with create_pgd_mapping_pmd later. */
-void create_pgd_mapping(pgd_t *pgdir, unsigned long va, unsigned long pa,
- unsigned long sz, unsigned long prot);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ASM_RISCV_PGTABLE_H */
diff --git a/na/arch/riscv/include/asm/sections.h b/na/arch/riscv/include/asm/sections.h
deleted file mode 100644
index 454d9de..0000000
--- a/na/arch/riscv/include/asm/sections.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _ASM_RISCV_SECTIONS_H
-#define _ASM_RISCV_SECTIONS_H
-
-/*
- * Linker-emitted symbols that bracket each section of the kernel image.
- *
- * The lumeos.lds.S linker script defines these as the start / end
- * addresses of contiguous regions. Code that walks a section
- * (relocate_kernel walks .rela.dyn, the C++ ctor loop walks
- * .init_array, the VMM mapping code walks .text vs .rodata, ...)
- * imports the pair via this header.
- *
- * Mirrors Linux arch/riscv/include/asm/sections.h / asm-generic/sections.h.
- * Each name and pair is identical to Linux so that direct ports of mm/init.c
- * relocate_kernel logic compile unchanged against LumeOS.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern char _start[];
-extern char _end[];
-
-extern char _stext[];
-extern char _etext[];
-extern char _srodata[];
-extern char _erodata[];
-extern char _sdata[];
-extern char _edata[];
-extern char _sbss[];
-extern char _ebss[];
-
-extern char __init_array_start[];
-extern char __init_array_end[];
-
-/* .rela.dyn — array of Elf64_Rela entries emitted by the linker under
- * `-shared -Bsymbolic`. relocate_kernel walks this range and patches
- * each R_RISCV_RELATIVE slot. */
-extern char __rela_dyn_start[];
-extern char __rela_dyn_end[];
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ASM_RISCV_SECTIONS_H */
diff --git a/na/arch/riscv/kernel/asm-offsets.c b/na/arch/riscv/kernel/asm-offsets.c
deleted file mode 100644
index edecab7..0000000
--- a/na/arch/riscv/kernel/asm-offsets.c
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-// Copyright (C) 2026 Richard Qin
-//
-// arch/riscv/kernel/asm-offsets.c — emit struct field offsets and
-// scalar constants for consumption by assembly sources (head.S etc.).
-//
-// 1:1 with Linux arch/riscv/kernel/asm-offsets.c. The Makefile
-// compiles this to an .s file (not .o) and runs
-// scripts/extract-asm-offsets.sed over the output to produce
-// include/generated/asm-offsets.h.
-//
-// Adding a new offset: include the struct's header, add an OFFSET()
-// or DEFINE() line below, rebuild. head.S can then reference the
-// generated #define directly.
-
-#include <lume/kbuild.h>
-#include <lume/kernel_map.h>
-
-void asm_offsets(void)
-{
- COMMENT("LumeOS asm-offsets — struct kernel_mapping field offsets");
- OFFSET(KERNEL_MAP_VIRT_ADDR, kernel_mapping, virt_addr);
- OFFSET(KERNEL_MAP_VIRT_OFFSET, kernel_mapping, virt_offset);
- OFFSET(KERNEL_MAP_PHYS_ADDR, kernel_mapping, phys_addr);
- OFFSET(KERNEL_MAP_SIZE, kernel_mapping, size);
- OFFSET(KERNEL_MAP_VA_PA_OFFSET, kernel_mapping, va_pa_offset);
- OFFSET(KERNEL_MAP_PAGE_OFFSET, kernel_mapping, page_offset);
- OFFSET(KERNEL_MAP_VA_KERNEL_PA_OFFSET, kernel_mapping, va_kernel_pa_offset);
-}
diff --git a/na/arch/riscv/kernel/head.S b/na/arch/riscv/kernel/head.S
deleted file mode 100644
index e04c44c..0000000
--- a/na/arch/riscv/kernel/head.S
+++ /dev/null
@@ -1,317 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * RISC-V kernel entry — Linux-aligned layout.
- *
- * Flow (mirrors arch/riscv/kernel/head.S in Linux):
- *
- * _start [.head.text, byte 0 of image]
- * · 64-byte Boot Protocol Image Header
- * · j real_start
- *
- * real_start [.text]
- * · save a0 (hartid) / a1 (dtb_pa)
- * · setup gp / per-hart sp
- * · BSP/AP election via amoswap
- * · BSP: clear BSS, call setup_vm, then relocate_enable_mmu
- * · AP: spin-wait, then relocate_enable_mmu
- *
- * relocate_enable_mmu [C function, arch/riscv/mm/init.cc]
- * · csrw satp <- trampoline_pg_dir
- * · jump to higher-half VA
- * · csrw satp <- early_pg_dir
- * · slide sp/ra/gp to higher-half
- * · call relocate_kernel (walks .rela.dyn, patches R_RISCV_RELATIVE)
- *
- * real_start (continued in higher-half)
- * · BSP only: call early_cpp_init (walks .init_array)
- * · call kernel_main(hartid, dtb_pa)
- *
- * Register contract from OpenSBI / U-Boot:
- * a0 = hart ID
- * a1 = FDT physical base
- */
-
-#include <lume/config.h>
-
-#define PAGE_OFFSET 0xFFFFFFC000000000
-#define BOOT_STACK_SIZE 8192
-
-/* ========================================================================
- * .head.text — RISC-V Boot Protocol Image Header
- *
- * Layout per Documentation/arch/riscv/boot-image-header.rst:
- * code0/code1 (8 B) : executable code, lands at PC after OpenSBI hand-off
- * text_offset (8 B) : load offset from RAM base
- * image_size (8 B) : effective image size (0 = unknown)
- * flags (8 B) : bit 0: endian
- * version (4 B) : major.minor
- * res1/res2 (4+8) : reserved
- * magic (8 B) : "RISCV\0\0\0"
- * magic2 (4 B) : "RSC\x05"
- * res3 (4 B) : PE/COFF reserved
- * ======================================================================== */
- .section .head.text, "ax"
- .globl _start
-_start:
- j real_start /* code0 */
- .word 0 /* code1 */
- .dword 0x200000 /* text_offset */
- .dword 0 /* image_size (post-link) */
- .dword 0 /* flags */
- .word 0x0002 /* version 0.2 */
- .word 0 /* res1 */
- .dword 0 /* res2 */
- .dword 0x5643534952 /* magic "RISCV\0\0\0" */
- .word 0x05435352 /* magic2 "RSC\x05" */
- .word 0 /* res3 */
-
-/* ========================================================================
- * real_start — all harts land here at physical PC
- * ======================================================================== */
- .text
- .globl real_start
-real_start:
- /* DEBUG: 'R<hartid>\n' — UART direct at 0x10000000, MMU off. */
- li t0, 0x10000000
- li t1, 'R'
- sb t1, 0(t0)
- addi t1, a0, '0'
- sb t1, 0(t0)
- li t1, '\n'
- sb t1, 0(t0)
-
- /* Mask interrupts and supervisor pending bits. */
- csrw sie, zero
- csrw sip, zero
-
- /* gp for medany — relax must be disabled across the `la`. */
- .option push
- .option norelax
- la gp, __global_pointer$
- .option pop
-
- /* per-hart boot stack: sp = boot_stack + (hartid + 1) * STACK_SIZE */
- la t0, boot_stack
- li t1, BOOT_STACK_SIZE
- addi t2, a0, 1
- mul t1, t1, t2
- add sp, t0, t1
-
- /* Save hartid / dtb_pa in callee-saved regs preserved across calls. */
- mv tp, a0 /* tp = hartid */
- mv s0, a1 /* s0 = dtb_pa */
-
- /* BSP/AP election: first hart to swap bsp_elected 0→1 is BSP. */
- la t0, bsp_elected
- li t1, 1
- amoswap.w.aq t2, t1, (t0)
- bnez t2, .Lap_wait /* prev != 0 → we're an AP */
-
- /* === BSP path === */
- la t0, bsp_hart_id
- sw a0, 0(t0)
-
- li t3, 0x10000000
- li t4, 'B'
- sb t4, 0(t3)
- li t4, '\n'
- sb t4, 0(t3)
-
- /* Clear BSS — must precede setup_vm, which writes kernel_map. */
- la t0, _sbss
- la t1, _ebss
-.Lbss_loop:
- bgeu t0, t1, .Lbss_done
- sd zero, 0(t0)
- addi t0, t0, 8
- j .Lbss_loop
-.Lbss_done:
-
- li t3, 0x10000000
- li t4, 'V'
- sb t4, 0(t3)
- li t4, '\n'
- sb t4, 0(t3)
-
- /* ---- BSP-only: pre-MMU PIC fixup of .rela.dyn ----
- *
- * The kernel is built with -fPIE -shared. All global-data accesses
- * compile to GOT-indirect loads (auipc + ld), where the linker
- * emits each .got slot's initial value as the link-time VA and a
- * companion R_RISCV_RELATIVE entry in .rela.dyn. At this point
- * we're running at the physical load address — those link-time VAs
- * are not yet mapped, so any global access would fault.
- *
- * Walk .rela.dyn in pure assembly (PC-relative only — no globals)
- * and patch every R_RISCV_RELATIVE slot to addend + (PA - LINK_VA).
- * After this, setup_vm can dereference kernel_map / early_pg_dir
- * / etc. via the GOT and get correct PA-side pointers.
- *
- * Mirrors Linux arch/riscv/kernel/head.S — same idea, same loop.
- *
- * reloc:
- * t0 = pa_offset = PA(_start) - KERNEL_LINK_ADDR (negative)
- * t1 = &__rela_dyn_start
- * t2 = &__rela_dyn_end
- * for each 24-byte rela in [t1, t2):
- * t3 = r_offset; t4 = r_info; t5 = r_addend
- * if (t4 & 0xFFFFFFFF) != R_RISCV_RELATIVE: skip
- * slot_pa = r_offset + pa_offset
- * *slot_pa = r_addend + pa_offset
- */
-#define R_RISCV_RELATIVE 3
-
- la t0, _start /* PA of _start */
- li t1, 0xFFFFFFC080200000 /* KERNEL_LINK_ADDR (asm/page.h) */
- sub t0, t0, t1 /* t0 = pa_offset (PA - LINK_VA) */
-
- la t1, __rela_dyn_start
- la t2, __rela_dyn_end
-.Lrela_loop:
- bgeu t1, t2, .Lrela_done
- ld t3, 0(t1) /* r_offset */
- ld t4, 8(t1) /* r_info */
- ld t5, 16(t1) /* r_addend */
- /* r_info low 32 bits = type */
- li t6, 0xFFFFFFFF
- and t4, t4, t6
- li t6, R_RISCV_RELATIVE
- bne t4, t6, .Lrela_next
- /* slot_pa = r_offset + pa_offset */
- add t3, t3, t0
- /* value = r_addend + pa_offset */
- add t5, t5, t0
- sd t5, 0(t3)
-.Lrela_next:
- addi t1, t1, 24 /* sizeof(Elf64_Rela) */
- j .Lrela_loop
-.Lrela_done:
-
- /* Memory fence so subsequent loads see the patched slots. */
- fence w, r
-
- li t3, 0x10000000
- li t4, 'P'
- sb t4, 0(t3)
- li t4, '\n'
- sb t4, 0(t3)
-
- /* Call setup_vm(load_pa = PA of _start, dtb_pa = s0).
- * Both args physical at this point — MMU still off. */
- la a0, _start
- mv a1, s0
- call setup_vm
-
- li t3, 0x10000000
- li t4, 'v'
- sb t4, 0(t3)
- li t4, '\n'
- sb t4, 0(t3)
-
- /* Release APs once page tables are built. */
- la t0, boot_sync_flag
- li t1, 1
- amoswap.w.rl zero, t1, (t0)
- j .Lenable_mmu
-
-/* ========================================================================
- * AP path — spin until BSP signals readiness.
- * ======================================================================== */
-.Lap_wait:
- la t0, boot_sync_flag
-.Lap_spin:
- lw t1, 0(t0)
- beqz t1, .Lap_spin
- fence r, rw
-
- /* fall through */
-
-/* ========================================================================
- * .Lenable_mmu — all harts: enable MMU, jump to higher-half, run reloc.
- *
- * relocate_enable_mmu does its own SATP write + higher-half jump in
- * C (with inline asm), then calls relocate_kernel. After it returns,
- * we are at higher-half VA with sp / ra / gp slid up, and .rela.dyn
- * is applied (so .init_array pointers are valid).
- * ======================================================================== */
-.Lenable_mmu:
- la a0, early_pg_dir
- call relocate_enable_mmu
- /* On return: PC, sp, ra, gp are all at higher-half VA. */
-
- li t3, 0x10000000
- li t4, 'M'
- sb t4, 0(t3)
- li t4, '\n'
- sb t4, 0(t3)
-
- /* Mark MMU enabled for console / pmap layers that check this. */
- call console_set_mmu_enabled
-
- /* BSP only: walk __init_array and call each C++ ctor. */
- la t0, bsp_hart_id
- lw t0, 0(t0)
- bne tp, t0, .Lskip_cpp_init
- call early_cpp_init
-.Lskip_cpp_init:
-
- /* Hand off to kernel_main(hartid, dtb_pa). */
- mv a0, tp
- mv a1, s0
- call kernel_main
-
- /* kernel_main should not return. */
-.Lhalt:
- csrc sstatus, 2
- wfi
- j .Lhalt
-
-/* ========================================================================
- * early_cpp_init — walk .init_array and call each constructor.
- *
- * Called only by BSP, after relocate_kernel has patched the function
- * pointers stored in __init_array. Before relocate_kernel runs those
- * pointers are zero-filled by the linker.
- * ======================================================================== */
- .globl early_cpp_init
-early_cpp_init:
- addi sp, sp, -32
- sd ra, 0(sp)
- sd s1, 8(sp)
- sd s2, 16(sp)
-
- la s1, __init_array_start
- la s2, __init_array_end
-.Linit_loop:
- bgeu s1, s2, .Linit_done
- ld t0, 0(s1)
- jalr ra, t0
- addi s1, s1, 8
- j .Linit_loop
-.Linit_done:
- ld ra, 0(sp)
- ld s1, 8(sp)
- ld s2, 16(sp)
- addi sp, sp, 32
- ret
-
-/* ========================================================================
- * Election state — in .data so explicit zero survives the BSP BSS clear
- * (these are read before BSS is cleared on the BSP, and concurrently by
- * APs while BSP is in the clear loop).
- * ======================================================================== */
- .section .data
- .align 2
- .globl boot_sync_flag
-boot_sync_flag:
- .word 0
-
- .globl bsp_elected
-bsp_elected:
- .word 0
-
- .globl bsp_hart_id
-bsp_hart_id:
- .word 0
diff --git a/na/arch/riscv/kernel/lumeos.lds.S b/na/arch/riscv/kernel/lumeos.lds.S
deleted file mode 100644
index 9bd083b..0000000
--- a/na/arch/riscv/kernel/lumeos.lds.S
+++ /dev/null
@@ -1,120 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * RISC-V Kernel Linker Script Template
- *
- * This is a C-preprocessed linker script (.lds.S). The build system
- * runs it through CPP to produce the final vmlinux.lds which is
- * passed to the linker.
- *
- * Architecture-specific definitions:
- * - OUTPUT_ARCH, ENTRY
- * - PAGE_OFFSET, BASE_ADDRESS, PHYS_BASE
- * - .head.text placement
- * - GP anchor (__global_pointer$ for RISC-V medany)
- * - early_pgdir and boot stacks in .bss
- *
- * Common section macros come from include/lume/vmlinux.lds.h
- *
- * Reference: docs/specs/linker_and_entry.md
- */
-
-#include <lume/lumeos.lds.h>
-
-/* ===== RISC-V Architecture Constants ===== */
-
-OUTPUT_ARCH(riscv)
-
-PAGE_OFFSET = 0xFFFFFFC000000000;
-BASE_ADDRESS = 0xFFFFFFC080200000;
-PHYS_BASE = 0x80200000;
-
-ENTRY(_start)
-
-/* ===== RISC-V: GP anchor in .data (medany model) ===== */
-#define ARCH_DATA_EXTRA \
- . = ALIGN(16); \
- PROVIDE(__global_pointer$ = . + 0x800); \
- *(.sdata .sdata.*)
-
-/* ===== RISC-V: page-table storage + per-CPU boot stacks in .bss =====
- *
- * Linux RISC-V layout (arch/riscv/kernel/vmlinux.lds.S):
- * early_pg_dir — first early page table built by setup_vm,
- * used for the rest of boot after MMU enable
- * trampoline_pg_dir — single-page table used only during the SATP
- * switch from physical to higher-half VA; its
- * identity + virt mappings of _start must both
- * be valid in the same instruction window
- * fixmap_pg_dir — covers the FIXADDR_START range used by the
- * early console (UART MMIO before kernel page
- * table proper is built)
- *
- * All three are allocated here so head.S can take their addresses without
- * a C definition; their PA == VA mapping covers them during setup_vm.
- */
-#define ARCH_BSS_EXTRA \
- . = ALIGN(4096); \
- early_pg_dir = .; \
- . += 4096; \
- trampoline_pg_dir = .; \
- . += 4096; \
- fixmap_pg_dir = .; \
- . += 4096; \
- /* Legacy name retained for any caller still referencing it; */ \
- /* points at the same first 4 KiB as early_pg_dir. */ \
- early_pgdir = early_pg_dir; \
- /* Per-CPU boot stacks: 8KB per CPU, max 8 CPUs */ \
- . = ALIGN(4096); \
- boot_stack = .; \
- . += 8192 * 8; \
- boot_stack_top = .;
-
-/* ===== Layout ===== */
-
-SECTIONS {
- . = BASE_ADDRESS;
-
- /* .head.text: Image Header (must be at byte 0 of the kernel image) */
- .head.text : AT(PHYS_BASE) {
- _start = .;
- KEEP(*(.head.text))
- }
-
- TEXT_SECTION
-
- . = ALIGN(4096);
-
- /* .rela.dyn lives between .text and .rodata so it ends up in the
- * read-only PT_LOAD segment. Mirrors Linux vmlinux.lds.S where
- * .rela.dyn sits at the tail of init data, just before RO_DATA. */
- RELA_DYN_SECTION
-
- . = ALIGN(4096);
-
- RODATA_SECTION
-
- . = ALIGN(4096);
-
- DATA_SECTION
-
- . = ALIGN(8);
-
- /* .got lives right after .data in the RW PT_LOAD. All other
- * dynamic-linker artefacts (.plt, .dynamic, .dynsym, .dynstr,
- * .hash, .gnu.hash, .interp, .dynbss) are discarded by the
- * DISCARDS macro — mirrors Linux arch/riscv/kernel/vmlinux.lds.S. */
- GOT_SECTION
-
- . = ALIGN(4096);
-
- /* End of file-backed image (BSS follows as NOBITS) */
- __lume_image_end = .;
-
- BSS_SECTION
-
- KERNEL_END
-
- DISCARDS
-}
diff --git a/na/arch/riscv/kernel/signal.cc b/na/arch/riscv/kernel/signal.cc
deleted file mode 100644
index 39fe34e..0000000
--- a/na/arch/riscv/kernel/signal.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * arch/riscv/signal.cc — Signal frame construction and sigreturn
- *
- * Constructs a signal frame on the user stack before returning to
- * the user signal handler. On sigreturn, restores the original
- * registers from the saved frame.
- *
- * Frame layout on user stack (grows downward):
- *
- * +-----------------------+ <- original sp
- * | sigset_t saved_mask | 8 bytes
- * | TrapFrame saved_regs | 296 bytes
- * | kernel_siginfo info | 128 bytes
- * +-----------------------+ <- new sp (16-byte aligned)
- *
- * Reference: Linux arch/riscv/kernel/signal.c
- */
-
-#include <lume/signal.h>
-#include <lume/signal_types.h>
-#include <lume/signal_nr.h>
-#include <lume/task.h>
-#include <lume/uaccess.h>
-#include <lume/errno.h>
-#include <lume/klog.h>
-#include <arch/trapframe.h>
-
-/* ================================================================== */
-/* Signal frame layout */
-/* ================================================================== */
-
-struct rt_sigframe {
- kernel_siginfo info; /* siginfo for SA_SIGINFO handlers */
- arch::TrapFrame saved_regs; /* saved register state */
- sigset_t saved_mask; /* blocked mask to restore on sigreturn */
-};
-
-/* ================================================================== */
-/* Signal trampoline (written to user stack) */
-/* ================================================================== */
-
-/*
- * When the user's signal handler returns (via `ret`), ra points here.
- * This tiny code sequence invokes sys_rt_sigreturn to restore the
- * original execution context.
- *
- * RISC-V instructions (8 bytes):
- * li a7, 139 → addi a7, zero, 139 → 0x08b00893
- * ecall → 0x00000073
- */
-static constexpr uint32 sigreturn_trampoline[2] = {
- 0x08b00893, /* li a7, 139 (SYS_rt_sigreturn) */
- 0x00000073, /* ecall */
-};
-
-/* ================================================================== */
-/* setup_signal_frame */
-/* ================================================================== */
-
-int setup_signal_frame(arch::TrapFrame* tf, int signo,
- kernel_sigaction* ka, kernel_siginfo* info)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) return -EINVAL;
-
- /* Calculate frame location on user stack */
- uint64 sp = tf->sp;
-
- /* Reserve space for the trampoline below the frame */
- sp -= sizeof(sigreturn_trampoline);
- sp &= ~15ULL; /* 16-byte align */
- uint64 trampoline_addr = sp;
-
- /* Reserve space for the signal frame */
- sp -= sizeof(rt_sigframe);
- sp &= ~15ULL; /* 16-byte align */
- uint64 frame_addr = sp;
-
- /* Build the frame in kernel memory, then copy to user stack */
- rt_sigframe frame{};
- if (info) {
- frame.info = *info;
- }
- frame.saved_regs = *tf;
- frame.saved_mask = curr->blocked;
-
- /* Copy frame to user stack */
- int err = copy_to_user(frame_addr, &frame, sizeof(frame));
- if (err < 0) return -EFAULT;
-
- /* Copy trampoline to user stack */
- err = copy_to_user(trampoline_addr, sigreturn_trampoline,
- sizeof(sigreturn_trampoline));
- if (err < 0) return -EFAULT;
-
- /* Set up the trap frame to execute the signal handler */
- tf->epc = ka->sa_handler;
- tf->sp = frame_addr;
-
- /* Arguments to the handler: handler(int signo, siginfo_t*, ucontext_t*) */
- tf->a[0] = static_cast<uint64>(signo);
-
- if (ka->sa_flags & SA_SIGINFO) {
- /* SA_SIGINFO: pass siginfo and ucontext pointers */
- tf->a[1] = frame_addr; /* &info */
- tf->a[2] = frame_addr + sizeof(kernel_siginfo); /* &saved_regs (as ucontext) */
- } else {
- tf->a[1] = 0;
- tf->a[2] = 0;
- }
-
- /* Return address: when the handler does `ret`, jump to trampoline */
- if (ka->sa_flags & SA_RESTORER) {
- tf->ra = ka->sa_restorer;
- } else {
- tf->ra = trampoline_addr;
- }
-
- return 0;
-}
-
-/* ================================================================== */
-/* arch_sigreturn — restore state from signal frame */
-/* ================================================================== */
-
-int64 arch_sigreturn(arch::TrapFrame* tf)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) return -EINVAL;
-
- /*
- * The signal frame is located at the current sp.
- * We need to read it back and restore the original state.
- */
- uint64 frame_addr = tf->sp;
-
- rt_sigframe frame{};
- int err = copy_from_user(&frame, frame_addr, sizeof(frame));
- if (err < 0) {
- printk(KERN_ERR "sigreturn: failed to read signal frame at %llx\n",
- frame_addr);
- task_kill_current(SIGSEGV);
- return -EFAULT;
- }
-
- /* Restore the saved register state */
- *tf = frame.saved_regs;
-
- /* Restore the blocked mask */
- curr->blocked = frame.saved_mask;
- curr->blocked.del(SIGKILL);
- curr->blocked.del(SIGSTOP);
-
- /*
- * Return value: we return the saved a0 from the original context.
- * The syscall dispatch will NOT overwrite a0 because sigreturn
- * is special — it returns via the restored trap frame directly.
- */
- return tf->a[0];
-}
diff --git a/na/arch/riscv/lib/random.cc b/na/arch/riscv/lib/random.cc
deleted file mode 100644
index afc0078..0000000
--- a/na/arch/riscv/lib/random.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <arch/random.h>
-#include <lume/klog.h>
-
-namespace arch {
-
-bool get_hw_random(uint64& value)
-{
- /*
- * Real implementation would check for Zkr extension and execute:
- * csrr x, seed
- *
- * Since QEMU virt doesn't guarantee Zkr, we return false to
- * force the MI layer to use the timer fallback.
- */
- return false;
-}
-
-} // namespace arch
diff --git a/na/arch/riscv/mm/init.cc b/na/arch/riscv/mm/init.cc
deleted file mode 100644
index 3cebcd9..0000000
--- a/na/arch/riscv/mm/init.cc
+++ /dev/null
@@ -1,241 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * RISC-V early memory-management bootstrap.
- *
- * Mirrors Linux arch/riscv/mm/init.c — same file name, same C function
- * names (setup_vm / relocate_enable_mmu / relocate_kernel), same global
- * state (kernel_map, early_pg_dir, trampoline_pg_dir). head.S calls
- * setup_vm immediately after BSS-clear, then later calls
- * relocate_enable_mmu, which switches SATP, jumps to higher-half VA,
- * and finally calls relocate_kernel to apply .rela.dyn.
- *
- * Differences from Linux:
- * - KASLR offset is hard-wired to 0 for now (no pi/ subsystem) — the
- * code path that would consume kernel_map.virt_offset is in place
- * so adding pi/-based seed reading later only requires populating
- * that one field.
- * - Only 1 GiB superpages supported in create_pgd_mapping; sufficient
- * for the early page tables. Finer-grained mappings come from
- * vmm_init() once slab is up.
- */
-
-#include <lume/init.h>
-#include <lume/setup.h>
-#include <lume/elf.h>
-#include <lume/kernel_map.h>
-#include <lume/types.h>
-#include <asm/page.h>
-#include <asm/csr.h>
-#include <asm/pgtable.h>
-#include <asm/sections.h>
-
-/*
- * Global kernel mapping descriptor — single source of truth for the
- * runtime-vs-link address relationship. Lives in .data so its initial
- * zero contents survive the BSS-clear that runs before setup_vm fills
- * the fields. Read by VMM, page-fault handler, ptdump, etc.
- *
- * Linux equivalent: arch/riscv/mm/init.c::kernel_map
- */
-struct kernel_mapping kernel_map __attribute__((section(".data")));
-
-/*
- * create_pgd_mapping — install one PGD entry covering a 1 GiB region.
- *
- * Only 1 GiB superpages are supported in this early variant; setup_vm
- * needs nothing finer. Identical interface to Linux's same-named
- * function in arch/riscv/mm/init.c, restricted in arity.
- */
-extern "C" void create_pgd_mapping(pgd_t *pgdir, unsigned long va,
- unsigned long pa, unsigned long sz,
- unsigned long prot)
-{
- /* This early variant only supports 1 GiB superpages. */
- if (sz != PGDIR_SIZE) {
- return;
- }
-
- /* SV39 PGD index = (va >> 30) & 0x1FF — see Volume II, §4.4.1.
- * Each PGD entry is 8 bytes; pgdir[i] is the slot for that VPN[2]. */
- unsigned long idx = (va >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1);
-
- /* PTE PPN field: physical page number shifted up by 10 (SV39 PTE
- * layout: bits [53:10] = PPN, bits [9:0] = flags). */
- unsigned long ppn = pa >> PAGE_SHIFT;
- pgdir[idx].pgd = (ppn << 10) | prot;
-}
-
-/*
- * setup_vm — fill kernel_map and build early_pg_dir + trampoline_pg_dir.
- *
- * Called from head.S after BSS is cleared, with:
- * load_pa = runtime physical address of _start
- * dtb_pa = physical address of the device tree blob (from a1)
- *
- * Mirrors Linux arch/riscv/mm/init.c::setup_vm. The actual SATP write
- * happens later in relocate_enable_mmu; here we only populate page
- * tables and the kernel_map descriptor.
- */
-extern "C" void __init setup_vm(uintptr_t load_pa, uintptr_t dtb_pa)
-{
- (void)dtb_pa; /* KASLR pi/ subsystem will consume this later. */
-
- /* --- 1. Fill kernel_map ----------------------------------------- */
-
- /* KASLR offset = 0 in this configuration. */
- kernel_map.virt_offset = 0;
- kernel_map.virt_addr = KERNEL_LINK_ADDR + kernel_map.virt_offset;
- kernel_map.phys_addr = load_pa;
- kernel_map.size = (uintptr_t)_end - (uintptr_t)_start;
- kernel_map.page_offset = PAGE_OFFSET;
-
- /* Mirrors Linux: va_pa_offset is the constant added to a physical
- * address to obtain its direct-map VA. */
- kernel_map.va_pa_offset = PAGE_OFFSET - load_pa;
-
- /* va_kernel_pa_offset is the constant added to a load PA to obtain
- * the kernel's link VA (used by relocate_enable_mmu to fix sp/ra). */
- kernel_map.va_kernel_pa_offset = kernel_map.virt_addr - load_pa;
-
- /* --- 2. Build early_pg_dir ------------------------------------- *
- *
- * SV39 root page table — 4 entries cover identity + higher-half for
- * both MMIO (PA 0x0000_0000, 1 GiB) and RAM (PA 0x8000_0000, 1 GiB):
- *
- * [0] identity PA 0x0000_0000 → VA 0x0000_0000
- * [2] identity PA 0x8000_0000 → VA 0x8000_0000
- * [256] high-half PA 0x0000_0000 → VA 0xFFFFFFC000000000
- * [258] high-half PA 0x8000_0000 → VA 0xFFFFFFC080000000
- *
- * Identity mappings keep PC valid right after csrw satp. High-half
- * mappings let the post-MMU `jr` to .Lhigher_half resolve. */
- create_pgd_mapping(early_pg_dir, 0x00000000UL, 0x00000000UL,
- PGDIR_SIZE, PAGE_KERNEL_EXEC);
- create_pgd_mapping(early_pg_dir, 0x80000000UL, 0x80000000UL,
- PGDIR_SIZE, PAGE_KERNEL_EXEC);
- create_pgd_mapping(early_pg_dir, PAGE_OFFSET + 0x00000000UL,
- 0x00000000UL, PGDIR_SIZE, PAGE_KERNEL_EXEC);
- create_pgd_mapping(early_pg_dir, PAGE_OFFSET + 0x80000000UL,
- 0x80000000UL, PGDIR_SIZE, PAGE_KERNEL_EXEC);
-
- /* --- 3. Build trampoline_pg_dir -------------------------------- *
- *
- * The trampoline maps just the gigapage containing _start, both at
- * its identity PA and at its higher-half VA. This is the page
- * table SATP holds during the single instruction window between
- * "MMU off" and "MMU on with early_pg_dir" — the PC must resolve
- * under both, so we re-use the same two entries above. */
- create_pgd_mapping(trampoline_pg_dir, load_pa & PGDIR_MASK,
- load_pa & PGDIR_MASK, PGDIR_SIZE, PAGE_KERNEL_EXEC);
- create_pgd_mapping(trampoline_pg_dir,
- (kernel_map.virt_addr) & PGDIR_MASK,
- load_pa & PGDIR_MASK, PGDIR_SIZE, PAGE_KERNEL_EXEC);
-}
-
-/*
- * relocate_kernel — walk .rela.dyn and apply R_RISCV_RELATIVE entries.
- *
- * Called from relocate_enable_mmu *after* the SATP switch and the jump
- * to higher-half VA — meaning __rela_dyn_start / __rela_dyn_end resolve
- * to valid higher-half pointers we can dereference directly.
- *
- * Mirrors Linux arch/riscv/mm/init.c::relocate_kernel. Key invariant:
- * even when kaslr_offset == 0, the slot store MUST still happen — the
- * linker leaves R_RISCV_RELATIVE slots zero-filled at link time, and
- * only relocate_kernel writes the link-time addend into them. Without
- * this pass, .init_array entries (and every other relocated symbol)
- * are NULL pointers.
- */
-void __init relocate_kernel(void)
-{
- auto *rela = (Elf64_Rela *)__rela_dyn_start;
- auto *rela_end = (Elf64_Rela *)__rela_dyn_end;
-
- const uint64 kaslr_offset = kernel_map.virt_offset;
-
- for (; rela < rela_end; ++rela) {
- if (ELF64_R_TYPE(rela->r_info) != R_RISCV_RELATIVE) {
- continue;
- }
- auto *slot = (Elf64_Addr *)(rela->r_offset + kaslr_offset);
- *slot = (Elf64_Addr)(rela->r_addend) + kaslr_offset;
- }
-}
-
-/*
- * relocate_enable_mmu — switch SATP, jump to higher-half, apply relocations.
- *
- * Called from head.S after setup_vm has filled kernel_map and built the
- * page tables. Equivalent to Linux's same-named asmlinkage routine but
- * written in C++ so it can share kernel_map / pgtable definitions with
- * setup_vm directly.
- *
- * Three logical steps:
- *
- * 1. csrw satp ← trampoline_pg_dir. PC continues running at the
- * identity PA, which the trampoline still maps; the high-half PA
- * mapping is now also live.
- *
- * 2. la t0, .Lvirt; add t0, t0, va_kernel_pa_offset; jr t0.
- * A PC-relative `la` under medany yields the runtime PA of the
- * label; adding va_kernel_pa_offset (= link_va - load_pa) converts
- * it to the link-time higher-half VA, which the trampoline maps.
- * The `jr` then atomically pushes PC into higher-half space.
- *
- * 3. csrw satp ← early_pg_dir. We're now executing at higher-half
- * VA, so the trampoline can be retired in favour of the full
- * early page table. sp / ra / gp must be slid to higher-half too,
- * otherwise the next ret-or-call uses physical-low addresses
- * whose mapping is about to disappear when vmm_init drops the
- * identity entries.
- *
- * 4. Call relocate_kernel — now safe, because all link-time VA
- * pointers in __rela_dyn_start[..] dereference correctly.
- */
-extern "C" void __init relocate_enable_mmu(pgd_t *pgdir)
-{
- (void)pgdir; /* head.S passes early_pg_dir for documentation; we
- * read the global symbol directly inside the asm. */
-
- const unsigned long va_kernel_pa_offset = kernel_map.va_kernel_pa_offset;
- const unsigned long satp_mode = SATP_MODE_SV39;
-
- asm volatile (
- /* Step 1: csrw satp <- trampoline */
- "la t0, trampoline_pg_dir\n"
- "srli t0, t0, 12\n"
- "or t0, t0, %[mode]\n"
- "csrw satp, t0\n"
- "sfence.vma\n"
-
- /* Step 2: jump to higher-half VA */
- "la t0, 1f\n"
- "add t0, t0, %[off]\n"
- "jr t0\n"
- "1:\n"
-
- /* Step 3a: csrw satp <- early_pg_dir (now in higher-half) */
- "la t0, early_pg_dir\n"
- "srli t0, t0, 12\n"
- "or t0, t0, %[mode]\n"
- "csrw satp, t0\n"
- "sfence.vma\n"
-
- /* Step 3b: slide sp / ra / gp to higher-half */
- "add sp, sp, %[off]\n"
- "add ra, ra, %[off]\n"
- ".option push\n"
- ".option norelax\n"
- "la gp, __global_pointer$\n"
- ".option pop\n"
- :
- : [off] "r"(va_kernel_pa_offset),
- [mode] "r"(satp_mode)
- : "t0", "memory"
- );
-
- /* Step 4: apply .rela.dyn now that VA dereferences work. */
- relocate_kernel();
-}
diff --git a/na/arch/riscv/mm/pmap.cc b/na/arch/riscv/mm/pmap.cc
deleted file mode 100644
index f0a6848..0000000
--- a/na/arch/riscv/mm/pmap.cc
+++ /dev/null
@@ -1,361 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * pmap.cc — SV39 page table operations (MD layer)
- *
- * Implements the pmap namespace interface for RISC-V SV39.
- * 3-level page table: VPN[2] (9 bits) -> VPN[1] (9 bits) -> VPN[0] (9 bits)
- * Each level is a 4KB page containing 512 PTEs of 8 bytes each.
- *
- * Address conversion: all page table operations work with physical addresses.
- * To read/write page table entries we must convert PA to VA via pa_to_va().
- *
- * Reference: docs/specs/vmm.md
- */
-
-#include <lume/types.h>
-#include <lume/addr.h>
-#include <lume/config.h>
-#include <lume/pmm.h>
-#include <lume/memblock.h>
-#include <arch/mmu.h>
-#include <lume/klog.h>
-#include <lume/errno.h>
-
-namespace pmap {
-
-/*
- * Before the first pmap::activate(), CPU runs on entry.S bootstrap page table
- * (fixed direct-map base). After activation, runtime (KASLR) base is valid.
- */
-static bool g_runtime_mapping_ready = false;
-
-/* PTE bit definitions */
-inline constexpr uint64 PTE_V = 1ULL << 0;
-inline constexpr uint64 PTE_R = 1ULL << 1;
-inline constexpr uint64 PTE_W = 1ULL << 2;
-inline constexpr uint64 PTE_X = 1ULL << 3;
-inline constexpr uint64 PTE_U = 1ULL << 4;
-inline constexpr uint64 PTE_G = 1ULL << 5;
-inline constexpr uint64 PTE_A = 1ULL << 6;
-inline constexpr uint64 PTE_D = 1ULL << 7;
-inline constexpr uint64 PTE_COW = 1ULL << 8;
-
-/* SV39: 3 levels, 9 bits per level, 12-bit page offset */
-inline constexpr int kLevels = 3;
-inline constexpr int kPtePerPage = 512;
-
-/* SV39: upper half of VA space (kernel) starts at L2 index 256 */
-inline constexpr int kKernelPgdStart = kPtePerPage / 2;
-
-/* SATP PPN mask (44 bits) and page shift */
-inline constexpr uint64 kSatpPpnMask = 0xFFFFFFFFFFFULL;
-inline constexpr int kPageShift = 12;
-
-/* Extract VPN[level] from a virtual address */
-static inline uint64 vpn(uint64 va, int level)
-{
- return (va >> (12 + 9 * level)) & 0x1FF;
-}
-
-/* Extract PA from a PTE */
-static inline uint64 pte_to_pa(uint64 pte)
-{
- return ((pte >> 10) & 0xFFFFFFFFFFFULL) << 12;
-}
-
-/* Create a PTE from a PA and flags */
-static inline uint64 pa_to_pte(uint64 pa, uint64 flags)
-{
- return ((pa >> 12) << 10) | flags;
-}
-
-/* Convert PA to kernel VA for accessing page table contents */
-static inline uint64 *pa_to_ptr(uint64 pa)
-{
- uint64 va = g_runtime_mapping_ready
- ? pa_to_va(phys_addr(pa)).raw
- : boot_pa_to_va(phys_addr(pa)).raw;
- return reinterpret_cast<uint64 *>(va);
-}
-
-/* Allocate a zeroed page for a page table level.
- * During early boot (before PMM), uses memblock.
- * After PMM init, uses the buddy allocator.
- * Returns PA of the new page, or 0 on failure. */
-static uint64 alloc_pt_page()
-{
- uint64 pa;
- if (memblock_is_active()) {
- pa = memblock_alloc(kPageSize, kPageSize);
- } else {
- Page *page = pmm_alloc_page();
- if (!page) {
- return 0;
- }
- pa = page_to_pa(page).raw;
- }
- if (!pa) {
- return 0;
- }
- /* Zero the page */
- uint64 *ptr = pa_to_ptr(pa);
- for (int i = 0; i < kPtePerPage; i++) {
- ptr[i] = 0;
- }
- return pa;
-}
-
-uint64 create()
-{
- uint64 new_root = alloc_pt_page();
- if (!new_root) return 0;
-
- /* Copy kernel mappings from the current kernel page table.
- * In SV39, entries 256..511 (VPN[2] >= 256) cover the upper
- * half of the 64-bit VA space where the kernel lives.
- * Without these, switching to the new page table would
- * immediately fault on any kernel VA access.
- *
- * Only do this after the runtime page table is active —
- * during early boot the bootstrap PT doesn't cover all of
- * RAM so pa_to_ptr on the kernel root would fault. */
- if (g_runtime_mapping_ready) {
- uint64 kernel_satp;
- asm volatile("csrr %0, satp" : "=r"(kernel_satp));
- uint64 kernel_root_pa = (kernel_satp & kSatpPpnMask) << kPageShift;
-
- uint64 *new_l2 = pa_to_ptr(new_root);
- uint64 *kern_l2 = pa_to_ptr(kernel_root_pa);
-
- for (int i = kKernelPgdStart; i < kPtePerPage; i++) {
- new_l2[i] = kern_l2[i];
- }
- }
-
- return new_root;
-}
-
-void destroy(uint64 root_pa)
-{
- uint64 *l2 = pa_to_ptr(root_pa);
-
- /* Only free the user half (entries 0..255).
- * Entries 256..511 are shared kernel PGD pointers copied
- * from the kernel page table — freeing them would corrupt
- * the kernel's own mappings. */
- for (int i = 0; i < kKernelPgdStart; i++) {
- if (!(l2[i] & PTE_V))
- continue;
- /* Non-leaf L2 entry? (no R/W/X bits = pointer to L1 table) */
- if (!(l2[i] & (PTE_R | PTE_W | PTE_X))) {
- uint64 l1_pa = pte_to_pa(l2[i]);
- uint64 *l1 = pa_to_ptr(l1_pa);
- for (int j = 0; j < kPtePerPage; j++) {
- if (!(l1[j] & PTE_V))
- continue;
- if (!(l1[j] & (PTE_R | PTE_W | PTE_X))) {
- /* L0 page table */
- uint64 l0_pa = pte_to_pa(l1[j]);
- pmm_free_page(pa_to_page(phys_addr(l0_pa)));
- }
- }
- pmm_free_page(pa_to_page(phys_addr(l1_pa)));
- }
- }
- pmm_free_page(pa_to_page(phys_addr(root_pa)));
-}
-
-int map(uint64 root_pa, uint64 va, uint64 pa, uint64 perm)
-{
- uint64 *table = pa_to_ptr(root_pa);
-
- /* Walk levels 2 -> 1, creating intermediate tables as needed */
- for (int level = 2; level > 0; level--) {
- uint64 idx = vpn(va, level);
- if (!(table[idx] & PTE_V)) {
- uint64 child_pa = alloc_pt_page();
- if (!child_pa)
- return -ENOMEM; /* -ENOMEM */
- table[idx] = pa_to_pte(child_pa, PTE_V);
- }
- table = pa_to_ptr(pte_to_pa(table[idx]));
- }
-
- /* Level 0: install the leaf PTE */
- uint64 idx = vpn(va, 0);
- table[idx] = pa_to_pte(pa, perm | PTE_V | PTE_A | PTE_D);
- return 0;
-}
-
-int map_2mb(uint64 root_pa, uint64 va, uint64 pa, uint64 perm)
-{
- /* VA and PA must be 2MB-aligned */
- constexpr uint64 k2MB = 2ULL * 1024 * 1024;
- if ((va & (k2MB - 1)) != 0 || (pa & (k2MB - 1)) != 0) {
- return -EINVAL; /* -EINVAL */
- }
-
- uint64 *table = pa_to_ptr(root_pa);
-
- /* Walk L2: create intermediate table if needed */
- uint64 idx2 = vpn(va, 2);
- if (!(table[idx2] & PTE_V)) {
- uint64 child_pa = alloc_pt_page();
- if (!child_pa) {
- return -ENOMEM; /* -ENOMEM */
- }
- table[idx2] = pa_to_pte(child_pa, PTE_V);
- }
- table = pa_to_ptr(pte_to_pa(table[idx2]));
-
- /* L1: install leaf PTE (2MB mega page) */
- uint64 idx1 = vpn(va, 1);
- table[idx1] = pa_to_pte(pa, perm | PTE_V | PTE_A | PTE_D);
- return 0;
-}
-
-void unmap(uint64 root_pa, uint64 va)
-{
- uint64 *table = pa_to_ptr(root_pa);
- for (int level = 2; level > 0; level--) {
- uint64 idx = vpn(va, level);
- if (!(table[idx] & PTE_V))
- return;
- table = pa_to_ptr(pte_to_pa(table[idx]));
- }
- table[vpn(va, 0)] = 0;
-}
-
-int protect(uint64 root_pa, uint64 va, uint64 perm)
-{
- uint64 *table = pa_to_ptr(root_pa);
- for (int level = 2; level >= 0; level--) {
- uint64 idx = vpn(va, level);
- uint64 pte = table[idx];
- if (!(pte & PTE_V))
- return -EFAULT; /* -EFAULT */
- if (pte & (PTE_R | PTE_W | PTE_X)) {
- uint64 pa = pte_to_pa(pte);
- table[idx] = pa_to_pte(pa, perm | PTE_V);
- return 0;
- }
- table = pa_to_ptr(pte_to_pa(pte));
- }
- return -EFAULT;
-}
-
-int protect_range(uint64 root_pa, uint64 start, uint64 end, uint64 perm)
-{
- if (end <= start) {
- return 0;
- }
-
- uint64 va = page_align_down(start);
- uint64 va_end = page_align_up(end);
-
- for (; va < va_end; va += kPageSize) {
- int ret = protect(root_pa, va, perm);
- if (ret == -14) {
- continue; /* unmapped page: skip */
- }
- if (ret < 0) {
- return ret;
- }
- }
-
- arch::mmu::flush_tlb_all();
- return 0;
-}
-
-bool lookup(uint64 root_pa, uint64 va, uint64 *pa_out)
-{
- uint64 *table = pa_to_ptr(root_pa);
- for (int level = 2; level >= 0; level--) {
- uint64 idx = vpn(va, level);
- uint64 pte = table[idx];
- if (!(pte & PTE_V))
- return false;
- /* Leaf PTE? (has R, W, or X) */
- if (pte & (PTE_R | PTE_W | PTE_X)) {
- if (pa_out)
- *pa_out = pte_to_pa(pte);
- return true;
- }
- table = pa_to_ptr(pte_to_pa(pte));
- }
- return false;
-}
-
-uint64 lookup_pte(uint64 root_pa, uint64 va)
-{
- uint64 *table = pa_to_ptr(root_pa);
- for (int level = 2; level >= 0; level--) {
- uint64 idx = vpn(va, level);
- uint64 pte = table[idx];
- if (!(pte & PTE_V))
- return 0;
- if (pte & (PTE_R | PTE_W | PTE_X))
- return pte; // leaf PTE raw value
- table = pa_to_ptr(pte_to_pa(pte));
- }
- return 0;
-}
-
-bool is_cow(uint64 root_pa, uint64 va)
-{
- uint64 *table = pa_to_ptr(root_pa);
- for (int level = 2; level >= 0; level--) {
- uint64 idx = vpn(va, level);
- uint64 pte = table[idx];
- if (!(pte & PTE_V))
- return false;
- if (pte & (PTE_R | PTE_W | PTE_X)) {
- return (pte & PTE_COW) != 0;
- }
- table = pa_to_ptr(pte_to_pa(pte));
- }
- return false;
-}
-
-int make_cow(uint64 root_pa, uint64 va)
-{
- uint64 *table = pa_to_ptr(root_pa);
- for (int level = 2; level >= 0; level--) {
- uint64 idx = vpn(va, level);
- uint64 pte = table[idx];
- if (!(pte & PTE_V))
- return -EFAULT; /* -EFAULT */
- if (pte & (PTE_R | PTE_W | PTE_X)) {
- /* Clear write bit, set COW bit */
- pte &= ~PTE_W;
- pte |= PTE_COW;
- table[idx] = pte;
- return 0;
- }
- table = pa_to_ptr(pte_to_pa(pte));
- }
- return -EFAULT;
-}
-
-void activate(uint64 root_pa)
-{
- arch::mmu::set_page_table(root_pa);
- arch::mmu::flush_tlb_all();
- g_runtime_mapping_ready = true;
-}
-
-uint64 vm_perm_to_pte(uint64 vm_perm)
-{
- /* Translate MI VM_* flags to SV39 PTE bits.
- * The MI flags are defined in <lume/vmm.h>. */
- uint64 pte = PTE_V | PTE_A | PTE_D; // Valid + Accessed + Dirty (avoid HW A/D faults)
- if (vm_perm & (1ULL << 0)) pte |= PTE_R; // VM_READ
- if (vm_perm & (1ULL << 1)) pte |= PTE_W; // VM_WRITE
- if (vm_perm & (1ULL << 2)) pte |= PTE_X; // VM_EXEC
- if (vm_perm & (1ULL << 3)) pte |= PTE_U; // VM_USER
- return pte;
-}
-
-} // namespace pmap
diff --git a/na/arch/riscv/mm/tlb_md.cc b/na/arch/riscv/mm/tlb_md.cc
deleted file mode 100644
index ed9aa29..0000000
--- a/na/arch/riscv/mm/tlb_md.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <arch/tlb.h>
-#include <arch/sbi.h>
-
-namespace arch::tlb {
-
-void flush_remote(uint64 cpu_mask, uint64 start, uint64 size) {
- if (size == ~0ULL) {
- arch::sbi::sbi_remote_sfence_vma(cpu_mask, 0, 0, ~0UL);
- } else {
- arch::sbi::sbi_remote_sfence_vma(cpu_mask, 0, start, size);
- }
-}
-
-} // namespace arch::tlb
diff --git a/na/arch/riscv/module.mk b/na/arch/riscv/module.mk
deleted file mode 100644
index 97d22de..0000000
--- a/na/arch/riscv/module.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-# arch/riscv/module.mk
-SRCS_CC += arch/riscv/mm/pmap.cc
-SRCS_CC += arch/riscv/mm/init.cc
-SRCS_CC += arch/riscv/config.cc
-SRCS_CC += arch/riscv/trap/trap_md.cc
-SRCS_CC += arch/riscv/syscall/syscall_md.cc
-SRCS_CC += arch/riscv/lib/random.cc
-SRCS_CC += arch/riscv/mm/tlb_md.cc
-SRCS_S += arch/riscv/kernel/head.S
-SRCS_S += arch/riscv/trap/swtch.S
-SRCS_S += arch/riscv/trap/fpu.S
-SRCS_S += arch/riscv/trap/trap_vector.S
-SRCS_S += arch/riscv/uaccess/uaccess_memcpy.S
-SRCS_CC += arch/riscv/trap/extable.cc
-SRCS_CC += arch/riscv/kernel/signal.cc
-
-# Arch-specific headers (added to include path)
-INCLUDES += -Iarch/riscv/include
diff --git a/na/arch/riscv/syscall/syscall_md.cc b/na/arch/riscv/syscall/syscall_md.cc
deleted file mode 100644
index 26320c8..0000000
--- a/na/arch/riscv/syscall/syscall_md.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Syscall MD Layer — RISC-V Register Extraction
- *
- * Extracts syscall number (a7) and arguments (a0-a5) from the TrapFrame,
- * calls the MI dispatcher, and writes the return value back to a0.
- *
- * Reference: docs/specs/syscall.md §5
- */
-
-#include <lume/syscall.h>
-#include <lume/syscall_nr.h>
-#include <arch/trapframe.h>
-#include <arch/cpu.h>
-
-void syscall_dispatch_md(arch::TrapFrame* tf)
-{
- uint64 nr = tf->a[7]; /* RISC-V ABI: syscall number in a7 */
-
- uint64 args[6] = {
- tf->a[0], tf->a[1], tf->a[2],
- tf->a[3], tf->a[4], tf->a[5],
- };
-
- /* Enable interrupts so timer and preemption work during syscalls */
- arch::cpu::intr_on();
-
- int64 ret = syscall_dispatch(nr, args);
-
- arch::cpu::intr_off();
-
- /* sys_rt_sigreturn restores the entire TrapFrame from the signal
- * frame on the user stack. Writing ret into a[0] would clobber
- * the restored value, so we must skip the writeback. */
- if (nr == static_cast<uint64>(SYS_rt_sigreturn)){
- return;
- }
- tf->a[0] = static_cast<uint64>(ret); /* Return value in a0 */
-}
-
diff --git a/na/arch/riscv/trap/extable.cc b/na/arch/riscv/trap/extable.cc
deleted file mode 100644
index 57a3dc1..0000000
--- a/na/arch/riscv/trap/extable.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <arch/extable.h>
-#include <lume/types.h>
-
-extern struct ExceptionTableEntry __extable_start[];
-extern struct ExceptionTableEntry __extable_end[];
-
-static void swap_extable_entries(struct ExceptionTableEntry *a, struct ExceptionTableEntry *b) {
- uint64 a_insn = reinterpret_cast<uint64>(&a->insn) + a->insn;
- uint64 a_fixup = reinterpret_cast<uint64>(&a->fixup) + a->fixup;
- uint64 b_insn = reinterpret_cast<uint64>(&b->insn) + b->insn;
- uint64 b_fixup = reinterpret_cast<uint64>(&b->fixup) + b->fixup;
-
- a->insn = static_cast<int32>(b_insn - reinterpret_cast<uint64>(&a->insn));
- a->fixup = static_cast<int32>(b_fixup - reinterpret_cast<uint64>(&a->fixup));
- b->insn = static_cast<int32>(a_insn - reinterpret_cast<uint64>(&b->insn));
- b->fixup = static_cast<int32>(a_fixup - reinterpret_cast<uint64>(&b->fixup));
-}
-
-static void sort_extable(size_t left, size_t right) {
- if (left >= right) return;
-
- size_t mid_idx = left + (right - left) / 2;
- struct ExceptionTableEntry *pivot = &__extable_start[mid_idx];
- uint64 pivot_val = reinterpret_cast<uint64>(&pivot->insn) + pivot->insn;
-
- size_t i = left;
- size_t j = right;
-
- while (i <= j) {
- while ((reinterpret_cast<uint64>(&__extable_start[i].insn) + __extable_start[i].insn) < pivot_val) {
- i++;
- }
- while ((reinterpret_cast<uint64>(&__extable_start[j].insn) + __extable_start[j].insn) > pivot_val) {
- if (j == 0) {
- break;
- }
- j--;
- }
- if (i <= j) {
- swap_extable_entries(&__extable_start[i], &__extable_start[j]);
- i++;
- if (j == 0) {
- break;
- }
- j--;
- }
- }
-
- if (j > left) {
- sort_extable(left, j);
- }
- if (i < right) {
- sort_extable(i, right);
- }
-}
-
-void extable_init() {
- size_t count = __extable_end - __extable_start;
- if (count <= 1) return;
- sort_extable(0, count - 1);
-}
-
-uint64 search_extable(uint64 pc) {
- size_t count = __extable_end - __extable_start;
- if (count == 0) return 0;
-
- size_t left = 0;
- size_t right = count - 1;
-
- /* Binary search */
- while (left <= right) {
- size_t mid = left + (right - left) / 2;
- const struct ExceptionTableEntry *entry = &__extable_start[mid];
- uint64 mid_pc = reinterpret_cast<uint64>(&entry->insn) + entry->insn;
-
- if (mid_pc == pc) {
- return reinterpret_cast<uint64>(&entry->fixup) + entry->fixup;
- }
-
- if (mid_pc < pc) {
- left = mid + 1;
- } else {
- if (mid == 0) break;
- right = mid - 1;
- }
- }
-
- return 0;
-}
diff --git a/na/arch/riscv/trap/fpu.S b/na/arch/riscv/trap/fpu.S
deleted file mode 100644
index 90d96d9..0000000
--- a/na/arch/riscv/trap/fpu.S
+++ /dev/null
@@ -1,103 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * FPU Save / Restore (RISC-V D extension)
- *
- * void fpu_save(FpState* state);
- * Saves f0-f31 and fcsr to the FpState structure.
- *
- * void fpu_restore(FpState* state);
- * Restores f0-f31 and fcsr from the FpState structure.
- *
- * FpState layout: f[0..31] at offset 0 (each 8 bytes), fcsr at offset 256.
- *
- * Called from schedule() when switching between tasks that have
- * used the FPU (sstatus.FS == Dirty on save path).
- */
-
- .section .text
-
-/* ======================================================================
- * void fpu_save(FpState* state); // a0 = state pointer
- * ====================================================================== */
- .globl fpu_save
- .align 2
-fpu_save:
- fsd f0, 0(a0)
- fsd f1, 8(a0)
- fsd f2, 16(a0)
- fsd f3, 24(a0)
- fsd f4, 32(a0)
- fsd f5, 40(a0)
- fsd f6, 48(a0)
- fsd f7, 56(a0)
- fsd f8, 64(a0)
- fsd f9, 72(a0)
- fsd f10, 80(a0)
- fsd f11, 88(a0)
- fsd f12, 96(a0)
- fsd f13, 104(a0)
- fsd f14, 112(a0)
- fsd f15, 120(a0)
- fsd f16, 128(a0)
- fsd f17, 136(a0)
- fsd f18, 144(a0)
- fsd f19, 152(a0)
- fsd f20, 160(a0)
- fsd f21, 168(a0)
- fsd f22, 176(a0)
- fsd f23, 184(a0)
- fsd f24, 192(a0)
- fsd f25, 200(a0)
- fsd f26, 208(a0)
- fsd f27, 216(a0)
- fsd f28, 224(a0)
- fsd f29, 232(a0)
- fsd f30, 240(a0)
- fsd f31, 248(a0)
- frcsr t0
- sw t0, 256(a0)
- ret
-
-/* ======================================================================
- * void fpu_restore(FpState* state); // a0 = state pointer
- * ====================================================================== */
- .globl fpu_restore
- .align 2
-fpu_restore:
- fld f0, 0(a0)
- fld f1, 8(a0)
- fld f2, 16(a0)
- fld f3, 24(a0)
- fld f4, 32(a0)
- fld f5, 40(a0)
- fld f6, 48(a0)
- fld f7, 56(a0)
- fld f8, 64(a0)
- fld f9, 72(a0)
- fld f10, 80(a0)
- fld f11, 88(a0)
- fld f12, 96(a0)
- fld f13, 104(a0)
- fld f14, 112(a0)
- fld f15, 120(a0)
- fld f16, 128(a0)
- fld f17, 136(a0)
- fld f18, 144(a0)
- fld f19, 152(a0)
- fld f20, 160(a0)
- fld f21, 168(a0)
- fld f22, 176(a0)
- fld f23, 184(a0)
- fld f24, 192(a0)
- fld f25, 200(a0)
- fld f26, 208(a0)
- fld f27, 216(a0)
- fld f28, 224(a0)
- fld f29, 232(a0)
- fld f30, 240(a0)
- fld f31, 248(a0)
- lw t0, 256(a0)
- fscsr t0
- ret
diff --git a/na/arch/riscv/trap/swtch.S b/na/arch/riscv/trap/swtch.S
deleted file mode 100644
index dbae43d..0000000
--- a/na/arch/riscv/trap/swtch.S
+++ /dev/null
@@ -1,96 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Context Switch & Kernel Thread Trampoline (RISC-V 64)
- *
- * context_switch: saves/restores callee-saved registers.
- * kthread_entry_trampoline: entry point for newly created kernel threads.
- * After context_switch loads the new Context, execution lands here via
- * Context.ra = kthread_entry_trampoline. The entry function and argument
- * are in s0 and s1 respectively, set by sched_init/create_tcb.
- *
- * The trampoline calls the MI-layer kthread_entry(entry, arg).
- *
- * Reference: docs/specs/scheduler.md §6
- */
-
- .section .text
-
-/* ========================================================================
- * void context_switch(Context* old_ctx, Context* next_ctx);
- * a0 = old_ctx_ptr, a1 = next_ctx_ptr
- * ======================================================================== */
- .globl context_switch
- .align 2
-context_switch:
- sd ra, 0(a0)
- sd sp, 8(a0)
- sd s0, 16(a0)
- sd s1, 24(a0)
- sd s2, 32(a0)
- sd s3, 40(a0)
- sd s4, 48(a0)
- sd s5, 56(a0)
- sd s6, 64(a0)
- sd s7, 72(a0)
- sd s8, 80(a0)
- sd s9, 88(a0)
- sd s10, 96(a0)
- sd s11, 104(a0)
-
- ld ra, 0(a1)
- ld sp, 8(a1)
- ld s0, 16(a1)
- ld s1, 24(a1)
- ld s2, 32(a1)
- ld s3, 40(a1)
- ld s4, 48(a1)
- ld s5, 56(a1)
- ld s6, 64(a1)
- ld s7, 72(a1)
- ld s8, 80(a1)
- ld s9, 88(a1)
- ld s10, 96(a1)
- ld s11, 104(a1)
-
- ret
-
-/* ========================================================================
- * kthread_entry_trampoline
- *
- * Reached via context_switch when a new kthread is first scheduled.
- * Context was set up by create_tcb():
- * s0 = entry function pointer
- * s1 = argument pointer
- *
- * Calls the MI-layer C++ function:
- * void kthread_entry(void (*entry)(void*), void* arg);
- * ======================================================================== */
- .globl kthread_entry_trampoline
- .align 2
-kthread_entry_trampoline:
- mv a0, s0 /* a0 = entry function */
- mv a1, s1 /* a1 = argument */
- call kthread_entry /* MI-layer C++ function */
-
- /* kthread_entry should never return, but if it does: */
- wfi
- j kthread_entry_trampoline + 12 /* loop on wfi */
-
-/* ========================================================================
- * ret_from_fork
- *
- * Trampoline for newly forked processes.
- * Context is set up by fork() so that s0 contains the TrapFrame pointer.
- *
- * Calls the MI-layer C++ function:
- * void fork_child_entry(TrapFrame* tf);
- * ======================================================================== */
- .globl ret_from_fork
- .align 2
-ret_from_fork:
- mv a0, s0 /* a0 = TrapFrame pointer */
- call fork_child_entry /* does not return, eventually calls userret */
- wfi
- j ret_from_fork + 8 \ No newline at end of file
diff --git a/na/arch/riscv/trap/trap_md.cc b/na/arch/riscv/trap/trap_md.cc
deleted file mode 100644
index e30aea6..0000000
--- a/na/arch/riscv/trap/trap_md.cc
+++ /dev/null
@@ -1,241 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Trap MD Layer (RISC-V 64)
- *
- * All architecture-specific trap logic lives here:
- * - scause/stval reading (CSR access)
- * - scause → MI TrapCause translation
- * - RISC-V specific fixups (ecall epc += 4, ebreak epc += 2)
- * - stvec setup (trap_init / trap_init_ap)
- * - kernel_trap_handler (traps from S-mode)
- *
- * MI layer (kernel/trap.cc) contains ZERO arch-specific code.
- *
- * Reference: docs/specs/trap.md §7
- */
-
-#include <lume/trap.h>
-#include <lume/irq.h>
-#include <lume/klog.h>
-#include <lume/console.h>
-#include <lume/kprintf.h>
-#include <lume/sched.h>
-#include <lume/task.h>
-#include <arch/trap.h>
-#include <arch/trapframe.h>
-#include <arch/cpu.h>
-#include <arch/extable.h>
-
-/* PLIC interface (drivers/plic.cc) */
-uint32 plic_claim();
-void plic_complete(uint32 irq);
-
-/* trap_vector.S: assembly entry point */
-extern "C" void trap_vector();
-
-/* ========================================================================
- * scause → TrapCause translation (RISC-V specific)
- * ======================================================================== */
-
-static TrapCause scause_to_trap_cause(uint64 scause)
-{
- if (arch::trap::is_interrupt(scause)) {
- switch (arch::trap::cause_code(scause)) {
- case IRQ_S_TIMER: return TrapCause::IrqTimer;
- case IRQ_S_SOFTWARE: return TrapCause::IrqSoftware;
- case IRQ_S_EXTERNAL: return TrapCause::IrqExternal;
- default: return TrapCause::Unknown;
- }
- }
-
- /* Exception (synchronous) */
- switch (scause) {
- case EXC_INST_PAGE_FAULT: return TrapCause::PageFaultExec;
- case EXC_LOAD_PAGE_FAULT: return TrapCause::PageFaultLoad;
- case EXC_STORE_PAGE_FAULT: return TrapCause::PageFaultStore;
- case EXC_ECALL_FROM_U: return TrapCause::Syscall;
- case EXC_ILLEGAL_INST: return TrapCause::IllegalInst;
- case EXC_BREAKPOINT: return TrapCause::Breakpoint;
-
- case EXC_INST_ACCESS_FAULT:
- case EXC_LOAD_ACCESS_FAULT:
- case EXC_STORE_ACCESS_FAULT:
- return TrapCause::AccessFault;
-
- case EXC_INST_MISALIGNED:
- case EXC_LOAD_MISALIGNED:
- case EXC_STORE_MISALIGNED:
- return TrapCause::AlignFault;
-
- default:
- return TrapCause::Unknown;
- }
-}
-
-TrapCause trap_translate_scause(uint64 scause)
-{
- return scause_to_trap_cause(scause);
-}
-
-static TrapOrigin origin_from_sstatus(uint64 sstatus)
-{
- constexpr uint64 kSstatusSPP = (1ULL << 8);
- return (sstatus & kSstatusSPP) ? TrapOrigin::FromKernel
- : TrapOrigin::FromUser;
-}
-
-static TrapClass classify_trap(uint64 scause, TrapCause cause)
-{
- if (arch::trap::is_interrupt(scause)) {
- return TrapClass::Interrupt;
- }
- if (cause == TrapCause::Syscall) {
- return TrapClass::Syscall;
- }
- if (cause == TrapCause::Unknown) {
- return TrapClass::Unknown;
- }
- return TrapClass::Exception;
-}
-
-static TrapInfo build_trap_info(arch::TrapFrame* tf,
- uint64 scause,
- uint64 stval,
- TrapOrigin origin)
-{
- TrapInfo info{};
- info.cause = scause_to_trap_cause(scause);
- info.cls = classify_trap(scause, info.cause);
- info.origin = origin;
- info.val = stval;
- info.epc = tf ? tf->epc : 0;
- info.scause = scause;
- return info;
-}
-
-/* ========================================================================
- * trap_dispatch — user-mode trap entry (called from trap_vector.S)
- *
- * Reads RISC-V CSRs, translates to MI TrapCause, applies arch-specific
- * fixups, then calls MI trap_handle().
- *
- * After trap_handle returns, we must return to user mode via userret().
- * The assembly jalr t3 that called us has NO return-to-user code after it,
- * so trap_dispatch must never return normally.
- * ======================================================================== */
-
-extern "C" void userret(arch::TrapFrame* tf);
-
-extern "C" [[noreturn]] void trap_dispatch(arch::TrapFrame* tf)
-{
- /* Switch stvec to kernelvec immediately so that any traps during
- * kernel execution (timer IRQs, page faults from copy_from_user)
- * go through the kernel trap entry, not the user trap entry.
- * This matches Linux which sets stvec to handle_exception (kernel)
- * at the top of the user trap path. */
- extern void kernelvec();
- arch::trap::set_vector(reinterpret_cast<uint64>(&kernelvec));
-
- /* Enable SUM so S-mode can access user pages (PTE_U).
- * Without this, any dereference of user-space pointers in syscall
- * handlers (sys_write buf, sys_read buf, etc.) would page fault. */
- arch::trap::enable_sum();
-
- uint64 scause = arch::trap::get_cause();
- uint64 stval = arch::trap::get_value();
-
- TrapOrigin origin = origin_from_sstatus(tf->sstatus);
- TrapInfo info = build_trap_info(tf, scause, stval, origin);
-
- /* Per-trap trace from user mode. Stored at KLOG_DEBUG / KLOG_SUBSYS_TRAP
- * so it lives in the ring buffer for postmortem but stays off the console
- * by default. No more in-band 20-trap window — flip console_level or
- * dump the ring on demand instead. */
- if (origin == TrapOrigin::FromUser) {
- pr_debug_s(KLOG_SUBSYS_TRAP,
- "trap: cause=%u epc=%llx val=%llx\n",
- static_cast<unsigned>(info.cause), info.epc, info.val);
- }
-
- /* Save trapframe in TCB for syscall handlers (like fork) */
- TaskControlBlock* curr = current_task();
- if (curr) {
- curr->trapframe = tf;
- }
-
- /* RISC-V ecall fix: ecall does not increment sepc.
- * Advance epc so sret returns to the instruction AFTER ecall. */
- if (info.cause == TrapCause::Syscall) {
- tf->epc += 4;
- info.epc = tf->epc;
- }
-
- /* RISC-V ebreak fix: advance past the C.EBREAK instruction (2 bytes).
- * This is arch-specific because instruction size depends on the ISA. */
- if (info.cause == TrapCause::Breakpoint) {
- tf->epc += 2;
- info.epc = tf->epc;
- }
-
- /* Call MI-layer dispatcher (zero inline assembly from here on) */
- trap_handle(tf, info);
-
- /* Return to user mode.
- * Set stvec back to trap_vector for the next user-mode trap,
- * then jump to userret which restores registers and does sret. */
- arch::trap::set_vector(reinterpret_cast<uint64>(&trap_vector));
- userret(tf);
- __builtin_unreachable();
-}
-
-/* ========================================================================
- * kernel_trap_handler — traps taken while already in S-mode
- *
- * For Phase 2, only timer and external interrupts are expected.
- * Exceptions in S-mode are always fatal.
- *
- * This belongs in MD because it reads scause directly.
- * ======================================================================== */
-
-extern "C" void kernel_trap_handler(arch::TrapFrame* tf)
-{
- uint64 scause = arch::trap::get_cause();
- uint64 stval = arch::trap::get_value();
-
- if (scause == EXC_LOAD_PAGE_FAULT || scause == EXC_STORE_PAGE_FAULT) {
- uint64 fixup = search_extable(tf->epc);
- if (fixup != 0) {
- /* Exception table entry found: redirect execution to fixup code */
- tf->epc = fixup;
- return;
- }
- }
-
- TrapInfo info = build_trap_info(tf, scause, stval, TrapOrigin::FromKernel);
- trap_handle(tf, info);
-}
-
-/* kernelvec assembly is in trap_vector.S */
-extern "C" void kernelvec();
-
-/* ========================================================================
- * trap_init() — BSP: set stvec, init IrqManager
- * ======================================================================== */
-
-void trap_init()
-{
- g_irq_manager.init();
- arch::trap::set_vector(reinterpret_cast<uint64>(&kernelvec));
-}
-
-/* ========================================================================
- * trap_init_ap() — AP: set this CPU's stvec
- * ======================================================================== */
-
-void trap_init_ap()
-{
- arch::trap::set_vector(reinterpret_cast<uint64>(&kernelvec));
-}
-
diff --git a/na/arch/riscv/trap/trap_vector.S b/na/arch/riscv/trap/trap_vector.S
deleted file mode 100644
index 8e45cae..0000000
--- a/na/arch/riscv/trap/trap_vector.S
+++ /dev/null
@@ -1,283 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Trap Vector Entry / Exit (RISC-V 64, S-mode)
- *
- * On trap entry (interrupt / exception / ecall):
- * sscratch holds the TrapFrame pointer (set up by kernel before sret).
- * We swap sscratch <-> tp, save all 31 GPRs + sstatus + sepc into TrapFrame,
- * then load kernel metadata (sp, satp, tp, trap handler) and jump to C++.
- *
- * On trap return (userret):
- * Restore all GPRs + sstatus + sepc from TrapFrame, swap sscratch <-> tp,
- * then sret back to user mode.
- *
- * Layout must match arch::TrapFrame in arch/riscv/include/arch/trapframe.h
- * Reference: docs/specs/arch_abstraction.md §7.2
- */
-
-#include <arch/trapframe.h>
-
- .section .text
- .globl trap_vector
- .globl userret
- .align 4
-
-/*
- * trap_vector — unified S-mode trap entry point (Direct mode)
- *
- * Precondition: sscratch = pointer to current task's TrapFrame
- */
-trap_vector:
- /* Step 1: swap sscratch <-> tp. Now tp = TrapFrame*, sscratch = old tp */
- csrrw tp, sscratch, tp
-
- /* Step 2: save all 31 general-purpose registers into TrapFrame */
- sd ra, TF_RA(tp)
- sd sp, TF_SP(tp)
- sd gp, TF_GP(tp)
- /* tp (x4) is in sscratch — save it later */
- sd t0, TF_T0(tp)
- sd t1, TF_T1(tp)
- sd t2, TF_T2(tp)
- sd s0, TF_S0(tp)
- sd s1, TF_S1(tp)
- sd a0, TF_A0(tp)
- sd a1, TF_A1(tp)
- sd a2, TF_A2(tp)
- sd a3, TF_A3(tp)
- sd a4, TF_A4(tp)
- sd a5, TF_A5(tp)
- sd a6, TF_A6(tp)
- sd a7, TF_A7(tp)
- sd s2, TF_S2(tp)
- sd s3, TF_S3(tp)
- sd s4, TF_S4(tp)
- sd s5, TF_S5(tp)
- sd s6, TF_S6(tp)
- sd s7, TF_S7(tp)
- sd s8, TF_S8(tp)
- sd s9, TF_S9(tp)
- sd s10, TF_S10(tp)
- sd s11, TF_S11(tp)
- sd t3, TF_T3(tp)
- sd t4, TF_T4(tp)
- sd t5, TF_T5(tp)
- sd t6, TF_T6(tp)
-
- /* Save the real tp (user's tp) which is now in sscratch */
- csrr t0, sscratch
- sd t0, TF_TP(tp)
-
- /* Step 3: save sstatus */
- csrr t0, sstatus
- sd t0, TF_SSTATUS(tp)
-
- /* Step 4: save sepc (interrupted PC) */
- csrr t0, sepc
- sd t0, TF_EPC(tp)
-
- /* Step 5: load kernel context from TrapFrame metadata */
- ld sp, TF_KERNEL_SP(tp) /* switch to kernel stack */
- ld t1, TF_KERNEL_SATP(tp) /* kernel page table */
- ld t2, TF_KERNEL_CPUID(tp) /* CPU ID for tp */
- ld t3, TF_KERNEL_TRAP(tp) /* C++ trap_handler address */
-
- /* Switch to kernel page table */
- csrw satp, t1
-
- /* Step 6: flush TLB after page table switch */
- sfence.vma zero, zero
-
- /* Step 6.5: restore kernel gp (global pointer).
- * The compiler uses gp-relative addressing for global variable access
- * (e.g. ld reg, offset(gp)). User mode may have set gp to anything,
- * so we MUST reload it before entering any C++ code. */
-.option push
-.option norelax
- la gp, __global_pointer$
-.option pop
-
- /* Pass TrapFrame* to C++ handler (a0 = tf) */
- mv a0, tp
-
- /* Set tp = CPU ID (kernel convention) */
- mv tp, t2
-
- /* Step 7: jump to C++ trap_handler */
- jalr t3
-
-
-/* ========================================================================
- * kernelvec — unified S-mode trap entry point for traps FROM S-MODE
- *
- * Traps from S-mode already have a valid kernel stack pointer in sp.
- * We just allocate a TrapFrame on the stack and save registers.
- * ======================================================================== */
- .globl kernelvec
- .align 4
-kernelvec:
- /* Allocate TrapFrame on stack */
- addi sp, sp, -296
-
- /* Save all 31 general-purpose registers into TrapFrame */
- sd ra, TF_RA(sp)
- /* Save the OLD sp (before we subtracted 296) */
- addi t0, sp, 296
- sd t0, TF_SP(sp)
- sd gp, TF_GP(sp)
- sd tp, TF_TP(sp)
- sd t0, TF_T0(sp)
- sd t1, TF_T1(sp)
- sd t2, TF_T2(sp)
- sd s0, TF_S0(sp)
- sd s1, TF_S1(sp)
- sd a0, TF_A0(sp)
- sd a1, TF_A1(sp)
- sd a2, TF_A2(sp)
- sd a3, TF_A3(sp)
- sd a4, TF_A4(sp)
- sd a5, TF_A5(sp)
- sd a6, TF_A6(sp)
- sd a7, TF_A7(sp)
- sd s2, TF_S2(sp)
- sd s3, TF_S3(sp)
- sd s4, TF_S4(sp)
- sd s5, TF_S5(sp)
- sd s6, TF_S6(sp)
- sd s7, TF_S7(sp)
- sd s8, TF_S8(sp)
- sd s9, TF_S9(sp)
- sd s10, TF_S10(sp)
- sd s11, TF_S11(sp)
- sd t3, TF_T3(sp)
- sd t4, TF_T4(sp)
- sd t5, TF_T5(sp)
- sd t6, TF_T6(sp)
-
- /* Save sstatus and sepc */
- csrr t0, sstatus
- sd t0, TF_SSTATUS(sp)
- csrr t0, sepc
- sd t0, TF_EPC(sp)
-
- /* Call C++ kernel_trap_handler(TrapFrame* tf) */
- mv a0, sp
- call kernel_trap_handler
-
- /* Restore sstatus and sepc */
- ld t0, TF_SSTATUS(sp)
- csrw sstatus, t0
- ld t0, TF_EPC(sp)
- csrw sepc, t0
-
- /* Restore all 31 general-purpose registers */
- ld ra, TF_RA(sp)
- ld gp, TF_GP(sp)
- ld tp, TF_TP(sp)
- ld t0, TF_T0(sp)
- ld t1, TF_T1(sp)
- ld t2, TF_T2(sp)
- ld s0, TF_S0(sp)
- ld s1, TF_S1(sp)
- ld a0, TF_A0(sp)
- ld a1, TF_A1(sp)
- ld a2, TF_A2(sp)
- ld a3, TF_A3(sp)
- ld a4, TF_A4(sp)
- ld a5, TF_A5(sp)
- ld a6, TF_A6(sp)
- ld a7, TF_A7(sp)
- ld s2, TF_S2(sp)
- ld s3, TF_S3(sp)
- ld s4, TF_S4(sp)
- ld s5, TF_S5(sp)
- ld s6, TF_S6(sp)
- ld s7, TF_S7(sp)
- ld s8, TF_S8(sp)
- ld s9, TF_S9(sp)
- ld s10, TF_S10(sp)
- ld s11, TF_S11(sp)
- ld t3, TF_T3(sp)
- ld t4, TF_T4(sp)
- ld t5, TF_T5(sp)
- ld t6, TF_T6(sp)
-
- /* Restore sp last */
- ld sp, TF_SP(sp)
-
- sret
-
-/*
- * userret — return to user mode
- *
- * Called from C++ as: userret(TrapFrame* tf)
- * a0 = pointer to TrapFrame (with user satp already written by caller)
- *
- * Precondition: interrupts must be disabled by caller.
- */
-userret:
- /* a0 = TrapFrame pointer, use it as base */
-
- /* Step 1: write back epc -> sepc */
- ld t0, TF_EPC(a0)
- csrw sepc, t0
-
- /* Step 2: write back sstatus */
- ld t0, TF_SSTATUS(a0)
- csrw sstatus, t0
-
- /* Step 3: caller is responsible for switching satp to user page table
- * and flushing TLB before calling userret. */
-
- /* Store TrapFrame address into sscratch for next trap entry */
- csrw sscratch, a0
-
- /* Use a0 as base to restore all registers */
- mv tp, a0
-
- /* Step 4: restore all 31 general-purpose registers */
- ld ra, TF_RA(tp)
- ld sp, TF_SP(tp)
- ld gp, TF_GP(tp)
- /* defer tp restore — we're using it as base */
- ld t0, TF_T0(tp)
- ld t1, TF_T1(tp)
- ld t2, TF_T2(tp)
- ld s0, TF_S0(tp)
- ld s1, TF_S1(tp)
- ld a0, TF_A0(tp)
- ld a1, TF_A1(tp)
- ld a2, TF_A2(tp)
- ld a3, TF_A3(tp)
- ld a4, TF_A4(tp)
- ld a5, TF_A5(tp)
- ld a6, TF_A6(tp)
- ld a7, TF_A7(tp)
- ld s2, TF_S2(tp)
- ld s3, TF_S3(tp)
- ld s4, TF_S4(tp)
- ld s5, TF_S5(tp)
- ld s6, TF_S6(tp)
- ld s7, TF_S7(tp)
- ld s8, TF_S8(tp)
- ld s9, TF_S9(tp)
- ld s10, TF_S10(tp)
- ld s11, TF_S11(tp)
- ld t3, TF_T3(tp)
- ld t4, TF_T4(tp)
- ld t5, TF_T5(tp)
- ld t6, TF_T6(tp)
-
- /* Step 5: restore user tp (swap sscratch <-> tp) */
- /* sscratch currently = TrapFrame address, tp = TrapFrame address */
- /* We need: tp = user's tp, sscratch = TrapFrame address */
- /* Load user tp from TrapFrame first, but we can't use any temp reg now.
- * Use the sscratch swap trick: */
- ld tp, TF_TP(tp)
-
- /* sscratch already holds TrapFrame address (set above with csrw) */
-
- /* Step 6: sret — return to user mode */
- sret
diff --git a/na/arch/riscv/uaccess/uaccess_memcpy.S b/na/arch/riscv/uaccess/uaccess_memcpy.S
deleted file mode 100644
index 615680d..0000000
--- a/na/arch/riscv/uaccess/uaccess_memcpy.S
+++ /dev/null
@@ -1,117 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <arch/extable.h>
-
-/*
- * extern "C" int __uaccess_memcpy(void* dst, const void* src, uint64 len);
- *
- * a0: dst
- * a1: src
- * a2: len
- * Returns: 0 on success, -EFAULT (-14) on page fault.
- */
-.global __uaccess_memcpy
-.type __uaccess_memcpy, @function
-__uaccess_memcpy:
- /* If len == 0, return 0 */
- beqz a2, .Lmemcpy_done
-
- /* Enable SUM (Supervisor User Memory) bit: sstatus.SUM is bit 18 */
- li t0, (1 << 18)
- csrs sstatus, t0
-
-.Lmemcpy_loop:
- /* Load byte from src */
-1: lb t1, 0(a1)
- /* Store byte to dst */
-2: sb t1, 0(a0)
-
- addi a0, a0, 1
- addi a1, a1, 1
- addi a2, a2, -1
- bnez a2, .Lmemcpy_loop
-
-.Lmemcpy_done:
- /* Disable SUM */
- li t0, (1 << 18)
- csrc sstatus, t0
- li a0, 0
- ret
-
-.Lmemcpy_fault:
- /* Fixup routine on fault */
- /* Disable SUM */
- li t0, (1 << 18)
- csrc sstatus, t0
- li a0, -14 /* -EFAULT */
- ret
-
-/* Exception table entries */
-EXTABLE(1b, .Lmemcpy_fault)
-EXTABLE(2b, .Lmemcpy_fault)
-
-
-/*
- * extern "C" int64 __uaccess_strncpy(char* dst, const char* src, uint64 max_len);
- *
- * a0: dst
- * a1: src
- * a2: max_len
- * Returns: number of bytes copied (excluding null), or -EFAULT (-14) on page fault.
- */
-.global __uaccess_strncpy
-.type __uaccess_strncpy, @function
-__uaccess_strncpy:
- /* If max_len == 0, return 0 */
- beqz a2, .Lstrncpy_done_zero
-
- /* Enable SUM */
- li t0, (1 << 18)
- csrs sstatus, t0
-
- /* t2 will store the length copied */
- li t2, 0
-
-.Lstrncpy_loop:
- /* Load byte from src */
-3: lb t1, 0(a1)
- /* Store byte to dst */
-4: sb t1, 0(a0)
-
- /* If it was the null terminator, we're done */
- beqz t1, .Lstrncpy_done
-
- addi a0, a0, 1
- addi a1, a1, 1
- addi t2, t2, 1
- addi a2, a2, -1
- bnez a2, .Lstrncpy_loop
-
- /* Reached max_len without finding null */
- /* We must null-terminate at max_len - 1 as per typical strncpy_from_user semantics */
- addi a0, a0, -1
- sb zero, 0(a0)
- addi t2, t2, -1 /* length of string without the forced null terminator */
-
-.Lstrncpy_done:
- /* Disable SUM */
- li t0, (1 << 18)
- csrc sstatus, t0
- mv a0, t2
- ret
-
-.Lstrncpy_done_zero:
- li a0, 0
- ret
-
-.Lstrncpy_fault:
- /* Fixup routine on fault */
- li t0, (1 << 18)
- csrc sstatus, t0
- li a0, -14 /* -EFAULT */
- ret
-
-/* Exception table entries */
-EXTABLE(3b, .Lstrncpy_fault)
-EXTABLE(4b, .Lstrncpy_fault)
diff --git a/na/drivers/base/bus.cc b/na/drivers/base/bus.cc
deleted file mode 100644
index 975f676..0000000
--- a/na/drivers/base/bus.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * drivers/base/bus.cc — bus_type registration.
- *
- * Mirrors Linux drivers/base/bus.c. Trivial in this iteration:
- * initialize the device/driver lists and the per-bus spinlock.
- *
- * Reference: docs/specs/storage_stack.md §4.3
- */
-
-#include <lume/device.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/errno.h>
-
-int bus_register(bus_type* bus)
-{
- if (bus == nullptr || bus->name == nullptr) {
- return -EINVAL;
- }
- if (bus->registered) {
- return 0;
- }
- bus->devices.init();
- bus->drivers.init();
- bus->lock.init(bus->name);
- bus->registered = true;
- kprintf("[bus] '%s' registered\n", bus->name);
- return 0;
-}
diff --git a/na/drivers/base/core.cc b/na/drivers/base/core.cc
deleted file mode 100644
index f92b2a4..0000000
--- a/na/drivers/base/core.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * drivers/base/core.cc — device core.
- *
- * Mirrors Linux drivers/base/core.c (greatly simplified). Provides
- * device_initialize, device_register, device_unregister, and the
- * try_bind helper shared with driver registration.
- *
- * Reference: docs/specs/storage_stack.md §4.3
- */
-
-#include <lume/device.h>
-#include <lume/container_of.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/errno.h>
-#include <lume/list.h>
-
-/* ------------------------------------------------------------------ */
-/* device ktype — generic release (no-op; subclasses override) */
-/* ------------------------------------------------------------------ */
-
-static void device_release(kobject* kobj)
-{
- /* Concrete device flavors (platform_device, virtio_device, ...) are
- * expected to allocate via kmalloc and free in their own release
- * paths after device_unregister + final kobject_put. The generic
- * device kobj_type only fires for stack-allocated devices used in
- * tests — nothing to do. */
- (void)kobj;
-}
-
-static const kobj_type device_ktype = {
- .release = device_release,
-};
-
-/* ------------------------------------------------------------------ */
-/* device_initialize / device_register */
-/* ------------------------------------------------------------------ */
-
-void device_initialize(device* dev)
-{
- if (dev == nullptr) {
- return;
- }
- kobject_init(&dev->kobj, &device_ktype);
- dev->parent = nullptr;
- dev->driver = nullptr;
- dev->driver_data = nullptr;
- dev->platform_data = nullptr;
- dev->devt = 0;
- dev->bus_list.init();
- dev->driver_list.init();
-}
-
-const char* dev_name(const device* dev)
-{
- if (dev == nullptr) {
- return "(null)";
- }
- if (dev->kobj.name != nullptr) {
- return dev->kobj.name;
- }
- if (dev->init_name != nullptr) {
- return dev->init_name;
- }
- return "(unnamed)";
-}
-
-/* try_bind: attempt to match `dev` against `drv` and probe.
- * Returns 1 on successful bind, 0 otherwise.
- * Caller must hold bus->lock. */
-int device_try_bind(device* dev, device_driver* drv);
-
-int device_try_bind(device* dev, device_driver* drv)
-{
- bus_type* bus = dev->bus;
-
- if (bus->match != nullptr && bus->match(dev, drv) == 0) {
- return 0;
- }
-
- dev->driver = drv;
-
- int rc = 0;
- if (bus->probe != nullptr) {
- rc = bus->probe(dev);
- } else if (drv->probe != nullptr) {
- rc = drv->probe(dev);
- }
-
- if (rc == 0) {
- list_add_tail(&dev->driver_list, &drv->devices);
- return 1;
- }
- dev->driver = nullptr;
- return 0;
-}
-
-int device_register(device* dev)
-{
- if (dev == nullptr || dev->bus == nullptr) {
- return -EINVAL;
- }
- if (!dev->bus->registered) {
- kprintf("[device] register '%s' fail: bus '%s' not registered\n",
- dev_name(dev),
- dev->bus->name != nullptr ? dev->bus->name : "?");
- return -ENODEV;
- }
-
- /* device_initialize is idempotent — safe to call again, but most
- * paths will already have initialized. Detect via ktype set. */
- if (dev->kobj.ktype == nullptr) {
- device_initialize(dev);
- }
- if (dev->kobj.name == nullptr) {
- kobject_add(&dev->kobj, nullptr, dev->init_name);
- }
- dev->bus_list.init();
- dev->driver_list.init();
-
- LockGuard g(dev->bus->lock);
- list_add_tail(&dev->bus_list, &dev->bus->devices);
-
- list_node* cur = dev->bus->drivers.next;
- while (cur != &dev->bus->drivers) {
- device_driver* drv =
- list_entry<device_driver, &device_driver::bus_node>(cur);
- cur = cur->next;
- if (device_try_bind(dev, drv) != 0) {
- break;
- }
- }
- return 0;
-}
-
-void device_unregister(device* dev)
-{
- if (dev == nullptr || dev->bus == nullptr) {
- return;
- }
-
- LockGuard g(dev->bus->lock);
-
- if (dev->driver != nullptr) {
- if (dev->driver->remove != nullptr) {
- dev->driver->remove(dev);
- }
- list_del_init(&dev->driver_list);
- dev->driver = nullptr;
- }
- list_del_init(&dev->bus_list);
-}
diff --git a/na/drivers/base/driver.cc b/na/drivers/base/driver.cc
deleted file mode 100644
index ce15b6d..0000000
--- a/na/drivers/base/driver.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * drivers/base/driver.cc — driver_register / driver_unregister.
- *
- * Mirrors Linux drivers/base/driver.c. Registers a device_driver on
- * its bus and tries to bind any unbound matching devices.
- *
- * Reference: docs/specs/storage_stack.md §4.3
- */
-
-#include <lume/device.h>
-#include <lume/container_of.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/errno.h>
-
-/* Implemented in core.cc — bind a device to a driver on its bus.
- * Returns 1 on success, 0 on no-match-or-probe-fail. */
-int device_try_bind(device* dev, device_driver* drv);
-
-int driver_register(device_driver* drv)
-{
- if (drv == nullptr || drv->bus == nullptr) {
- return -EINVAL;
- }
- if (!drv->bus->registered) {
- kprintf("[driver] register '%s' fail: bus '%s' not registered\n",
- drv->name != nullptr ? drv->name : "?",
- drv->bus->name != nullptr ? drv->bus->name : "?");
- return -ENODEV;
- }
-
- drv->bus_node.init();
- drv->devices.init();
-
- LockGuard g(drv->bus->lock);
- list_add_tail(&drv->bus_node, &drv->bus->drivers);
-
- /* Try to bind any unbound devices already on the bus. */
- list_node* cur = drv->bus->devices.next;
- while (cur != &drv->bus->devices) {
- device* dev = list_entry<device, &device::bus_list>(cur);
- cur = cur->next;
- if (dev->driver == nullptr) {
- device_try_bind(dev, drv);
- }
- }
- return 0;
-}
-
-void driver_unregister(device_driver* drv)
-{
- if (drv == nullptr || drv->bus == nullptr) {
- return;
- }
-
- LockGuard g(drv->bus->lock);
-
- /* Unbind every device bound to this driver. */
- while (!drv->devices.is_empty()) {
- list_node* first = drv->devices.next;
- device* dev = list_entry<device, &device::driver_list>(first);
- if (drv->remove != nullptr) {
- drv->remove(dev);
- }
- list_del_init(&dev->driver_list);
- dev->driver = nullptr;
- }
- list_del_init(&drv->bus_node);
-}
diff --git a/na/drivers/base/module.mk b/na/drivers/base/module.mk
deleted file mode 100644
index 1d8fb05..0000000
--- a/na/drivers/base/module.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (C) 2026 Richard Qin
-#
-# Driver model core (mirrors Linux drivers/base/)
-
-SRCS_CC += drivers/base/bus.cc
-SRCS_CC += drivers/base/core.cc
-SRCS_CC += drivers/base/driver.cc
-SRCS_CC += drivers/base/platform.cc
diff --git a/na/drivers/base/platform.cc b/na/drivers/base/platform.cc
deleted file mode 100644
index 65ec21e..0000000
--- a/na/drivers/base/platform.cc
+++ /dev/null
@@ -1,231 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * drivers/base/platform.cc — platform bus + FDT-driven device creation.
- *
- * Mirrors Linux drivers/base/platform.c. Provides:
- * - platform_bus — the bus_type instance
- * - platform_match — compatible-string matching via of_match_table
- * - platform_dispatch — bus->probe forwarder that down-casts and calls
- * platform_driver::probe(platform_device*)
- * - driver_probe_all() — boot entry: register bus, register linker-set
- * drivers, walk FDT, register platform_devices
- *
- * Reference: docs/specs/storage_stack.md §4.3
- */
-
-#include <lume/platform_device.h>
-#include <lume/driver.h>
-#include <lume/device_node.h>
-#include <lume/fdt.h>
-#include <string.h>
-
-/* kmalloc is declared extern "C" in <lume/slab.h>, but mm/slab.cc defines
- * it WITHOUT extern "C" (i.e. C++-mangled). Declare locally with C++
- * linkage so the symbol matches what mm/slab.o exports. */
-void* kmalloc(uint32 size);
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/errno.h>
-#include <lume/container_of.h>
-
-/* ------------------------------------------------------------------ */
-/* Linker-section registry */
-/* ------------------------------------------------------------------ */
-
-extern "C" {
- extern platform_driver* __drivers_start[];
- extern platform_driver* __drivers_end[];
-}
-
-/* ------------------------------------------------------------------ */
-/* platform_bus_type */
-/* ------------------------------------------------------------------ */
-
-static int platform_match(device* dev, device_driver* drv)
-{
- platform_device* pdev = to_platform_device(dev);
- platform_driver* pdrv = to_platform_driver(drv);
-
- if (pdrv->of_match_table == nullptr || pdev->of_node == nullptr) {
- return 0;
- }
- const char* node_compat = pdev->of_node->get_prop_string("compatible");
- if (node_compat == nullptr) {
- return 0;
- }
- for (const of_device_id* id = pdrv->of_match_table;
- id->compatible != nullptr; ++id) {
- if (strcmp(id->compatible, node_compat) == 0) {
- return 1;
- }
- }
- return 0;
-}
-
-static int platform_dispatch(device* dev)
-{
- platform_device* pdev = to_platform_device(dev);
- platform_driver* pdrv = to_platform_driver(dev->driver);
- if (pdrv->probe == nullptr) {
- return 0;
- }
- return pdrv->probe(pdev);
-}
-
-bus_type platform_bus = {
- .name = "platform",
- .match = platform_match,
- .probe = platform_dispatch,
- .devices = {},
- .drivers = {},
- .lock = {},
- .registered = false,
-};
-
-void platform_bus_init()
-{
- bus_register(&platform_bus);
-}
-
-/* ------------------------------------------------------------------ */
-/* platform_driver_register / platform_device_register */
-/* ------------------------------------------------------------------ */
-
-int platform_driver_register(platform_driver* pdrv)
-{
- if (pdrv == nullptr) {
- return -EINVAL;
- }
- pdrv->driver.bus = &platform_bus;
- /* driver.probe is unused — we route through bus->probe. Set name
- * from the first of_match if not already provided. */
- if (pdrv->driver.name == nullptr && pdrv->of_match_table != nullptr) {
- pdrv->driver.name = pdrv->of_match_table[0].compatible;
- }
- return driver_register(&pdrv->driver);
-}
-
-int platform_device_register(platform_device* pdev)
-{
- if (pdev == nullptr) {
- return -EINVAL;
- }
- pdev->dev.bus = &platform_bus;
- if (pdev->dev.init_name == nullptr && pdev->of_node != nullptr) {
- pdev->dev.init_name = pdev->of_node->get_prop_string("compatible");
- }
- device_initialize(&pdev->dev);
- return device_register(&pdev->dev);
-}
-
-/* ------------------------------------------------------------------ */
-/* FDT-driven device instantiation */
-/* ------------------------------------------------------------------ */
-
-static platform_device* alloc_platform_device_from_node(DeviceNode* node,
- const char* compat)
-{
- auto* pdev = static_cast<platform_device*>(
- kmalloc(static_cast<uint32>(sizeof(platform_device))));
- if (pdev == nullptr) {
- return nullptr;
- }
- memset(pdev, 0, sizeof(*pdev));
- pdev->of_node = node;
- pdev->mmio_base = node->get_prop_u64("reg", 0);
- /* "reg" second cell is size — for now we don't bother parsing it
- * separately; drivers that need it pull from of_node directly. */
- pdev->mmio_size = 0;
- pdev->irq = -1;
- pdev->dev.init_name = compat;
- return pdev;
-}
-
-/* For each registered platform_driver, walk its of_match_table and
- * spawn a platform_device for each FDT compatible match found.
- *
- * NOTE: This is a simplified FDT walker — it only finds the FIRST FDT
- * node matching each compatible string (delegating to
- * fdt_find_compatible). Devices with multiple FDT instances of the
- * same compatible will only see one bound here. A full FDT tree walk
- * is left for when virtio-mmio or multi-instance UART arrives.
- */
-static void instantiate_devices_for_driver(platform_driver* pdrv)
-{
- if (pdrv == nullptr || pdrv->of_match_table == nullptr) {
- return;
- }
- for (const of_device_id* id = pdrv->of_match_table;
- id->compatible != nullptr; ++id) {
- DeviceNode* node = fdt_find_compatible(id->compatible);
- if (node == nullptr) {
- continue;
- }
- platform_device* pdev =
- alloc_platform_device_from_node(node, id->compatible);
- if (pdev == nullptr) {
- kprintf("[platform] OOM allocating pdev for %s\n", id->compatible);
- continue;
- }
- kprintf("[platform] instantiating '%s' -> pdev mmio=0x%llx\n",
- id->compatible, pdev->mmio_base);
- platform_device_register(pdev);
- }
-}
-
-/* ------------------------------------------------------------------ */
-/* Boot entry — replaces the old driver_probe_all() */
-/* ------------------------------------------------------------------ */
-
-void driver_probe_all()
-{
- kprintf("[dpa] step 1: &platform_bus=%p (link 0xffffffc08033a0a8)\n",
- &platform_bus);
- kprintf("[dpa] step 1: pre-touch platform_bus.name slot...\n");
- const char** name_slot = &platform_bus.name;
- kprintf("[dpa] step 1: name_slot=%p\n", name_slot);
- kprintf("[dpa] step 1: about to deref *name_slot\n");
- const char* nm = *name_slot;
- kprintf("[dpa] step 1: *name_slot=%p\n", nm);
- kprintf("[dpa] step 1: about to read first byte of nm\n");
- char first = nm != nullptr ? nm[0] : '!';
- kprintf("[dpa] step 1: nm[0]='%c'\n", first);
-
- kprintf("[dpa] step 1: platform_bus_init enter\n");
- platform_bus_init();
- kprintf("[dpa] step 1: platform_bus_init done\n");
-
- platform_driver** start = __drivers_start;
- platform_driver** end = __drivers_end;
- kprintf("[dpa] step 2: __drivers_start=%p __drivers_end=%p count=%lld\n",
- start, end, (long long)(end - start));
-
- for (platform_driver** slot = start; slot < end; ++slot) {
- kprintf("[dpa] step 2: slot=%p *slot=%p\n", slot, *slot);
- if (*slot != nullptr) {
- platform_driver* pdrv = *slot;
- kprintf("[dpa] step 2: pdrv->of_match_table=%p\n",
- pdrv->of_match_table);
- if (pdrv->of_match_table != nullptr) {
- kprintf("[dpa] step 2: pdrv->of_match_table[0].compatible=%p\n",
- pdrv->of_match_table[0].compatible);
- if (pdrv->of_match_table[0].compatible != nullptr) {
- kprintf("[dpa] step 2: compatible='%s'\n",
- pdrv->of_match_table[0].compatible);
- }
- }
- platform_driver_register(pdrv);
- kprintf("[dpa] step 2: registered\n");
- }
- }
-
- kprintf("[dpa] step 3: instantiate devices\n");
- for (platform_driver** slot = start; slot < end; ++slot) {
- if (*slot != nullptr) {
- instantiate_devices_for_driver(*slot);
- }
- }
- kprintf("[dpa] done\n");
-}
diff --git a/na/drivers/console.cc b/na/drivers/console.cc
deleted file mode 100644
index f741dd2..0000000
--- a/na/drivers/console.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * console.cc — Console character device (/dev/console)
- *
- * Wraps the existing early_putc() UART driver as a file_operations
- * chardev. read() is a stub for now (returns 0 = EOF).
- * write() sends each byte through early_putc().
- */
-
-#include <lume/file.h>
-#include <lume/chardev.h>
-#include <lume/types.h>
-
-extern "C" void early_putc(char c);
-
-/* ================================================================== */
-/* console_write */
-/* ================================================================== */
-
-static int64 console_write(file* fp, const char* buf, uint64 count, int64* pos)
-{
- (void)fp;
- (void)pos;
-
- for (uint64 i = 0; i < count; i++) {
- early_putc(buf[i]);
- }
-
- return static_cast<int64>(count);
-}
-
-/* ================================================================== */
-/* console_read — stub (returns 0 = EOF for now) */
-/* ================================================================== */
-
-static int64 console_read(file* fp, char* buf, uint64 count, int64* pos)
-{
- (void)fp;
- (void)buf;
- (void)count;
- (void)pos;
- /* TODO: implement UART RX polling/IRQ-based blocking read */
- return 0;
-}
-
-/* ================================================================== */
-/* null device — /dev/null */
-/* ================================================================== */
-
-static int64 null_read(file* /*fp*/, char* /*buf*/, uint64 /*count*/, int64* /*pos*/)
-{
- return 0; /* EOF */
-}
-
-static int64 null_write(file* /*fp*/, const char* /*buf*/, uint64 count, int64* /*pos*/)
-{
- return static_cast<int64>(count); /* discard */
-}
-
-/* ================================================================== */
-/* zero device — /dev/zero */
-/* ================================================================== */
-
-static int64 zero_read(file* /*fp*/, char* buf, uint64 count, int64* /*pos*/)
-{
- for (uint64 i = 0; i < count; i++) {
- buf[i] = 0;
- }
- return static_cast<int64>(count);
-}
-
-static int64 zero_write(file* /*fp*/, const char* /*buf*/, uint64 count, int64* /*pos*/)
-{
- return static_cast<int64>(count); /* discard */
-}
-
-/* ================================================================== */
-/* Registration — runtime init (no static initializers!) */
-/* ================================================================== */
-
-static file_operations console_fops;
-static file_operations null_fops;
-static file_operations zero_fops;
-
-static cdev console_cdev;
-static cdev null_cdev;
-static cdev zero_cdev;
-
-void console_cdev_init()
-{
- /* Console: major=5, minor=1 */
- console_fops.read = console_read;
- console_fops.write = console_write;
-
- console_cdev.name = "console";
- console_cdev.dev = MKDEV(5, 1);
- console_cdev.fops = &console_fops;
- cdev_register(&console_cdev);
-
- /* /dev/null: major=1, minor=3 */
- null_fops.read = null_read;
- null_fops.write = null_write;
-
- null_cdev.name = "null";
- null_cdev.dev = MKDEV(1, 3);
- null_cdev.fops = &null_fops;
- cdev_register(&null_cdev);
-
- /* /dev/zero: major=1, minor=5 */
- zero_fops.read = zero_read;
- zero_fops.write = zero_write;
-
- zero_cdev.name = "zero";
- zero_cdev.dev = MKDEV(1, 5);
- zero_cdev.fops = &zero_fops;
- cdev_register(&zero_cdev);
-}
diff --git a/na/drivers/module.mk b/na/drivers/module.mk
deleted file mode 100644
index 9326f30..0000000
--- a/na/drivers/module.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (C) 2026 Richard Qin
-#
-# Drivers module
-
-SRCS_CC += drivers/uart.cc
-SRCS_CC += drivers/console.cc
-
-ifeq ($(ARCH),riscv)
-SRCS_CC += drivers/plic.cc
-endif
diff --git a/na/drivers/plic.cc b/na/drivers/plic.cc
deleted file mode 100644
index 2818700..0000000
--- a/na/drivers/plic.cc
+++ /dev/null
@@ -1,259 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * plic.cc — Platform-Level Interrupt Controller (RISC-V)
- *
- * QEMU virt platform PLIC:
- * Base PA: 0x0C000000
- * Size: 0x04000000 (64MB)
- *
- * Register layout (per RISC-V PLIC spec):
- * 0x000000: Priority registers (source 0..1023, 4 bytes each)
- * 0x001000: Pending bits (source 0..1023, bit per source)
- * 0x002000: Enable bits per context
- * 0x200000: Threshold + claim/complete per context
- *
- * Context mapping for QEMU virt:
- * Context 2*hartid + 1 = S-mode context for hart N
- *
- * Reference: docs/specs/trap.md §5.1
- */
-
-#include <arch/config.h>
-#include <arch/cpu.h>
-#include <lume/addr.h>
-#include <lume/config.h>
-#include <lume/fdt.h>
-#include <lume/platform_device.h>
-#include <lume/irq.h>
-#include <lume/types.h>
-#include <lume/vmm.h>
-
-/* PLIC virtual base (after MMIO mapping) */
-static uint64 plic_va;
-
-extern "C" uint32 bsp_hart_id;
-
-/* PLIC register offsets */
-static constexpr uint64 PLIC_PRIORITY_BASE = 0x000000;
-static constexpr uint64 PLIC_PENDING_BASE = 0x001000;
-static constexpr uint64 PLIC_ENABLE_BASE = 0x002000;
-static constexpr uint64 PLIC_THRESHOLD_BASE = 0x200000;
-static constexpr uint64 PLIC_CLAIM_BASE = 0x200004;
-
-/* Each context's enable/threshold/claim region is 0x80 bytes apart for enable,
- * and 0x1000 bytes apart for threshold/claim. */
-static constexpr uint64 PLIC_ENABLE_STRIDE = 0x80;
-static constexpr uint64 PLIC_CONTEXT_STRIDE = 0x1000;
-
-/* Dynamic S-mode context mapping */
-static uint32 g_s_context[kMaxCpus];
-
-static inline uint64 s_context(uint64 hart_id) {
- if (hart_id < kMaxCpus)
- return g_s_context[hart_id];
- return 2 * hart_id + 1; /* safe fallback */
-}
-
-static bool str_equals_plic(const char *a, const char *b) {
- if (!a || !b)
- return false;
- while (*a && *a == *b) {
- a++;
- b++;
- }
- return *a == *b;
-}
-
-/* Read a 32-bit MMIO register */
-static inline uint32 plic_read32(uint64 offset) {
- return *reinterpret_cast<volatile uint32 *>(plic_va + offset);
-}
-
-/* Write a 32-bit MMIO register */
-static inline void plic_write32(uint64 offset, uint32 val) {
- *reinterpret_cast<volatile uint32 *>(plic_va + offset) = val;
-}
-
-/* ========================================================================
- * Public API
- * ======================================================================== */
-
-static void plic_probe(DeviceNode* node) {
- uint64 plic_pa = 0;
-
- /* Initialize default fallback mapping */
- for (uint32 i = 0; i < kMaxCpus; i++) {
- g_s_context[i] = 2 * i + 1;
- }
-
- /* Dynamically discover PLIC base address and context mapping from FDT */
- if (node) {
- plic_pa = node->get_prop_u64("reg", plic_pa);
-
- /* Build phandle to hart_id map */
- struct PhandleMap {
- uint32 phandle;
- uint32 hart_id;
- } pmap[kMaxCpus];
- int num_cpus = 0;
-
- DeviceNode *cpus_node = fdt_get_node_by_path("/cpus");
- if (cpus_node) {
- list_node *cur = cpus_node->children_list_head();
- list_node *end = cpus_node->children_list_sentinel();
- while (cur != end && num_cpus < kMaxCpus) {
- DeviceNode *cpu =
- list_entry<DeviceNode, &DeviceNode::sibling_link_>(cur);
- cur = cur->next;
-
- const char *type = cpu->get_prop_string("device_type");
- if (type && str_equals_plic(type, "cpu")) {
- uint32 hart_id = cpu->get_prop_u32("reg");
- DeviceNode *intc = cpu->find_child("interrupt-controller");
- if (intc) {
- uint32 phandle = intc->get_prop_u32("phandle");
- if (phandle == 0)
- phandle = intc->get_prop_u32("linux,phandle");
- if (phandle != 0) {
- pmap[num_cpus].phandle = phandle;
- pmap[num_cpus].hart_id = hart_id;
- num_cpus++;
- }
- }
- }
- }
- }
-
- /* Parse interrupts-extended */
- const FdtProperty *ie_prop = node->get_prop("interrupts-extended");
- if (ie_prop && ie_prop->len > 0) {
- uint32 len = ie_prop->len;
- const uint8 *val = static_cast<const uint8 *>(ie_prop->value);
- int context_idx = 0;
- /* Each entry is 2 cells (8 bytes): [phandle, interrupt_type] */
- for (uint32 i = 0; i + 7 < len; i += 8) {
- uint32 phandle = (uint32(val[i]) << 24) | (uint32(val[i + 1]) << 16) |
- (uint32(val[i + 2]) << 8) | val[i + 3];
- uint32 irq_type = (uint32(val[i + 4]) << 24) |
- (uint32(val[i + 5]) << 16) |
- (uint32(val[i + 6]) << 8) | val[i + 7];
-
- if (irq_type == 9) { /* IRQ_S_EXTERNAL */
- for (int h = 0; h < num_cpus; h++) {
- if (pmap[h].phandle == phandle) {
- if (pmap[h].hart_id < kMaxCpus) {
- g_s_context[pmap[h].hart_id] = context_idx;
- }
- break;
- }
- }
- }
- context_idx++;
- }
- }
- }
-
- if (plic_pa == 0) return;
-
- /* Map PLIC MMIO */
- vmm_map_kernel_mmio(plic_pa, 0x4000000);
- plic_va = pa_to_va(phys_addr(plic_pa)).raw;
-
- /* Use the actual BSP hart ID (it may not be hart 0) */
- uint64 ctx = s_context(bsp_hart_id);
-
- /* Set priority for all sources to 1 (minimum non-zero = enabled) */
- for (uint32 irq = 1; irq < IrqManager::kMaxIrqs; irq++) {
- plic_write32(PLIC_PRIORITY_BASE + irq * 4, 1);
- }
-
- /* Set threshold to 0 for BSP context (accept all priorities > 0) */
- plic_write32(PLIC_THRESHOLD_BASE + ctx * PLIC_CONTEXT_STRIDE, 0);
-}
-
-static void plic_init_ap() {
- uint64 ctx = s_context(arch::cpu::id());
-
- /* Set threshold to 0 for this AP's context */
- plic_write32(PLIC_THRESHOLD_BASE + ctx * PLIC_CONTEXT_STRIDE, 0);
-}
-
-static void plic_enable(uint32 irq) {
- if (irq == 0 || irq >= IrqManager::kMaxIrqs) {
- return;
- }
-
- uint64 ctx = s_context(arch::cpu::id());
- uint64 offset = PLIC_ENABLE_BASE + ctx * PLIC_ENABLE_STRIDE;
- uint32 word_index = irq / 32;
- uint32 bit_index = irq % 32;
-
- uint32 val = plic_read32(offset + word_index * 4);
- val |= (1U << bit_index);
- plic_write32(offset + word_index * 4, val);
-}
-
-static void plic_disable(uint32 irq) {
- if (irq == 0 || irq >= IrqManager::kMaxIrqs) {
- return;
- }
-
- uint64 ctx = s_context(arch::cpu::id());
- uint64 offset = PLIC_ENABLE_BASE + ctx * PLIC_ENABLE_STRIDE;
- uint32 word_index = irq / 32;
- uint32 bit_index = irq % 32;
-
- uint32 val = plic_read32(offset + word_index * 4);
- val &= ~(1U << bit_index);
- plic_write32(offset + word_index * 4, val);
-}
-
-static void plic_set_priority(uint32 irq, uint32 priority) {
- if (irq == 0 || irq >= IrqManager::kMaxIrqs)
- return;
- plic_write32(PLIC_PRIORITY_BASE + (irq * 4), priority);
-}
-
-static uint32 plic_claim() {
- uint64 ctx = s_context(arch::cpu::id());
- return plic_read32(PLIC_CLAIM_BASE + ctx * PLIC_CONTEXT_STRIDE);
-}
-
-static void plic_complete(uint32 irq) {
- uint64 ctx = s_context(arch::cpu::id());
- plic_write32(PLIC_CLAIM_BASE + ctx * PLIC_CONTEXT_STRIDE, irq);
-}
-
-static const IrqChip plic_chip = {
- "riscv,plic0",
- plic_claim,
- plic_complete,
- plic_init_ap,
- plic_enable,
- plic_disable,
- plic_set_priority
-};
-
-static int plic_platform_probe(platform_device* pdev) {
- plic_probe(pdev->of_node);
- g_irq_manager.set_chip(&plic_chip);
- return 0;
-}
-
-static const of_device_id plic_of_match[] = {
- { .compatible = "riscv,plic0", .data = nullptr },
- { .compatible = nullptr, .data = nullptr }, /* sentinel */
-};
-
-platform_driver plic_driver = {
- .driver = { .name = "plic", .bus = nullptr,
- .probe = nullptr, .remove = nullptr,
- .bus_node = {}, .devices = {} },
- .probe = plic_platform_probe,
- .remove = nullptr,
- .of_match_table = plic_of_match,
-};
-
-PLATFORM_DRIVER(plic_driver);
diff --git a/na/drivers/tty/module.mk b/na/drivers/tty/module.mk
deleted file mode 100644
index 49190d4..0000000
--- a/na/drivers/tty/module.mk
+++ /dev/null
@@ -1,5 +0,0 @@
-# drivers/tty/module.mk — TTY subsystem build rules
-
-SRCS_CC += drivers/tty/tty_core.cc
-SRCS_CC += drivers/tty/n_tty.cc
-SRCS_CC += drivers/tty/tty_uart.cc
diff --git a/na/drivers/tty/n_tty.cc b/na/drivers/tty/n_tty.cc
deleted file mode 100644
index 0e312b0..0000000
--- a/na/drivers/tty/n_tty.cc
+++ /dev/null
@@ -1,539 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * n_tty.cc — N_TTY line discipline
- *
- * Implements the standard terminal line discipline:
- *
- * Canonical mode (ICANON):
- * Input is buffered until newline/EOF. Backspace (VERASE) and
- * kill-line (VKILL) editing are supported.
- *
- * Raw mode (!ICANON):
- * Characters are available immediately (subject to VMIN/VTIME).
- *
- * Signal generation (ISIG):
- * VINTR → SIGINT, VQUIT → SIGQUIT, VSUSP → SIGTSTP
- *
- * Echo (ECHO):
- * Typed characters are reflected to output.
- *
- * Input processing (c_iflag):
- * ICRNL: CR → NL mapping
- * INLCR: NL → CR mapping
- * IGNCR: ignore CR
- * ISTRIP: strip high bit
- *
- * Output processing (c_oflag):
- * OPOST + ONLCR: NL → CR+NL on output
- *
- * Reference: Linux drivers/tty/n_tty.c
- */
-
-#include <lume/tty.h>
-#include <lume/tty_ioctl.h>
-#include <lume/signal.h>
-#include <lume/signal_nr.h>
-#include <lume/slab.h>
-#include <lume/new.h>
-#include <lume/errno.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-
-#ifndef ENOIOCTLCMD
-#define ENOIOCTLCMD 515
-#endif
-
-extern void schedule();
-
-/* ================================================================== */
-/* N_TTY internal data */
-/* ================================================================== */
-
-inline constexpr int N_TTY_BUF_SIZE = 4096;
-
-struct n_tty_data {
- char read_buf[N_TTY_BUF_SIZE];
- int read_head; /* producer writes here (from receive_buf) */
- int read_tail; /* consumer reads from here (from read()) */
- int canon_head; /* in canonical mode: end of last complete line */
- bool eof_pending; /* Ctrl+D was pressed */
- Spinlock lock;
-};
-
-static inline int buf_count(const n_tty_data* d)
-{
- return (d->read_head - d->read_tail + N_TTY_BUF_SIZE) % N_TTY_BUF_SIZE;
-}
-
-static inline int canon_count(const n_tty_data* d)
-{
- return (d->canon_head - d->read_tail + N_TTY_BUF_SIZE) % N_TTY_BUF_SIZE;
-}
-
-static inline bool buf_full(const n_tty_data* d)
-{
- return buf_count(d) >= N_TTY_BUF_SIZE - 1;
-}
-
-static inline void buf_put(n_tty_data* d, char c)
-{
- d->read_buf[d->read_head] = c;
- d->read_head = (d->read_head + 1) % N_TTY_BUF_SIZE;
-}
-
-static inline char buf_get(n_tty_data* d)
-{
- char c = d->read_buf[d->read_tail];
- d->read_tail = (d->read_tail + 1) % N_TTY_BUF_SIZE;
- return c;
-}
-
-/* ================================================================== */
-/* Output helpers */
-/* ================================================================== */
-
-static void tty_put_char(tty_struct* tty, char c)
-{
- if (tty->driver && tty->driver->ops && tty->driver->ops->put_char) {
- tty->driver->ops->put_char(tty, c);
- }
-}
-
-/*
- * Process and output a single character, applying c_oflag transformations.
- */
-static void tty_output_char(tty_struct* tty, char c)
-{
- if (tty->termios_data.c_oflag & OPOST) {
- if (c == '\n' && (tty->termios_data.c_oflag & ONLCR)) {
- tty_put_char(tty, '\r');
- tty_put_char(tty, '\n');
- return;
- }
- if (c == '\r' && (tty->termios_data.c_oflag & OCRNL)) {
- tty_put_char(tty, '\n');
- return;
- }
- }
- tty_put_char(tty, c);
-}
-
-/*
- * Echo a character back to the terminal.
- * Control characters are displayed as ^X.
- */
-static void echo_char(tty_struct* tty, char c)
-{
- if (!(tty->termios_data.c_lflag & ECHO))
- return;
-
- if (c == '\n') {
- tty_output_char(tty, '\n');
- return;
- }
-
- if (c == '\t') {
- tty_put_char(tty, '\t');
- return;
- }
-
- /* Control characters: echo as ^X */
- if ((tty->termios_data.c_lflag & ECHOCTL) && c < 0x20 && c != '\t' && c != '\n') {
- tty_put_char(tty, '^');
- tty_put_char(tty, c + '@');
- return;
- }
-
- if (c == 0x7f) { /* DEL */
- tty_put_char(tty, '^');
- tty_put_char(tty, '?');
- return;
- }
-
- tty_output_char(tty, c);
-}
-
-/*
- * Erase one character visually (backspace + space + backspace).
- */
-static void erase_char(tty_struct* tty, char erased)
-{
- if (!(tty->termios_data.c_lflag & ECHO))
- return;
-
- if ((tty->termios_data.c_lflag & ECHOE)) {
- /* If the erased char was a control char displayed as ^X, erase 2 cols */
- if ((erased < 0x20 && erased != '\t' && erased != '\n') || erased == 0x7f) {
- tty_put_char(tty, '\b');
- tty_put_char(tty, ' ');
- tty_put_char(tty, '\b');
- tty_put_char(tty, '\b');
- tty_put_char(tty, ' ');
- tty_put_char(tty, '\b');
- } else {
- tty_put_char(tty, '\b');
- tty_put_char(tty, ' ');
- tty_put_char(tty, '\b');
- }
- }
-}
-
-/* ================================================================== */
-/* Input processing — receive_buf (called from IRQ or driver) */
-/* ================================================================== */
-
-static void n_tty_receive_buf(tty_struct* tty, const char* cp, int count)
-{
- auto* d = static_cast<n_tty_data*>(tty->ldisc.data);
- if (!d) return;
-
- const termios& t = tty->termios_data;
-
- LockGuard guard(d->lock);
-
- for (int i = 0; i < count; i++) {
- char c = cp[i];
-
- /* Strip high bit if requested */
- if (t.c_iflag & ISTRIP)
- c &= 0x7f;
-
- /* Input CR/NL processing */
- if (c == '\r') {
- if (t.c_iflag & IGNCR)
- continue;
- if (t.c_iflag & ICRNL)
- c = '\n';
- } else if (c == '\n') {
- if (t.c_iflag & INLCR)
- c = '\r';
- }
-
- /* Signal generation */
- if (t.c_lflag & ISIG) {
- if (c == t.c_cc[VINTR]) {
- tty_signal_pgrp(tty, SIGINT);
- echo_char(tty, c);
- if (!(t.c_lflag & NOFLSH)) {
- /* Flush input buffer */
- d->read_head = d->read_tail;
- d->canon_head = d->read_tail;
- }
- continue;
- }
- if (c == t.c_cc[VQUIT]) {
- tty_signal_pgrp(tty, SIGQUIT);
- echo_char(tty, c);
- if (!(t.c_lflag & NOFLSH)) {
- d->read_head = d->read_tail;
- d->canon_head = d->read_tail;
- }
- continue;
- }
- if (c == t.c_cc[VSUSP]) {
- tty_signal_pgrp(tty, SIGTSTP);
- echo_char(tty, c);
- continue;
- }
- }
-
- /* Canonical mode processing */
- if (t.c_lflag & ICANON) {
- /* EOF (Ctrl+D) */
- if (c == t.c_cc[VEOF]) {
- d->eof_pending = true;
- d->canon_head = d->read_head;
- tty->read_wait.wake_all();
- continue;
- }
-
- /* ERASE (backspace/DEL) */
- if (c == t.c_cc[VERASE]) {
- if (d->read_head != d->canon_head &&
- d->read_head != d->read_tail) {
- int prev = (d->read_head - 1 + N_TTY_BUF_SIZE) % N_TTY_BUF_SIZE;
- char erased = d->read_buf[prev];
- d->read_head = prev;
- erase_char(tty, erased);
- }
- continue;
- }
-
- /* KILL (Ctrl+U — erase entire line) */
- if (c == t.c_cc[VKILL]) {
- while (d->read_head != d->canon_head &&
- d->read_head != d->read_tail) {
- int prev = (d->read_head - 1 + N_TTY_BUF_SIZE) % N_TTY_BUF_SIZE;
- char erased = d->read_buf[prev];
- d->read_head = prev;
- erase_char(tty, erased);
- }
- if (t.c_lflag & ECHOK) {
- tty_output_char(tty, '\n');
- }
- continue;
- }
-
- /* WERASE (Ctrl+W — erase word) */
- if ((t.c_lflag & IEXTEN) && c == t.c_cc[VWERASE]) {
- /* Skip trailing spaces */
- while (d->read_head != d->canon_head) {
- int prev = (d->read_head - 1 + N_TTY_BUF_SIZE) % N_TTY_BUF_SIZE;
- if (d->read_buf[prev] != ' ' && d->read_buf[prev] != '\t')
- break;
- erase_char(tty, d->read_buf[prev]);
- d->read_head = prev;
- }
- /* Erase word */
- while (d->read_head != d->canon_head) {
- int prev = (d->read_head - 1 + N_TTY_BUF_SIZE) % N_TTY_BUF_SIZE;
- if (d->read_buf[prev] == ' ' || d->read_buf[prev] == '\t')
- break;
- erase_char(tty, d->read_buf[prev]);
- d->read_head = prev;
- }
- continue;
- }
-
- /* REPRINT (Ctrl+R) */
- if ((t.c_lflag & IEXTEN) && c == t.c_cc[VREPRINT]) {
- tty_output_char(tty, '\n');
- int pos = d->canon_head;
- while (pos != d->read_head) {
- echo_char(tty, d->read_buf[pos]);
- pos = (pos + 1) % N_TTY_BUF_SIZE;
- }
- continue;
- }
-
- /* Regular character — buffer it */
- if (!buf_full(d)) {
- buf_put(d, c);
- echo_char(tty, c);
- }
-
- /* Line complete on newline or EOL */
- if (c == '\n' || c == t.c_cc[VEOL] ||
- (t.c_cc[VEOL2] && c == t.c_cc[VEOL2])) {
- d->canon_head = d->read_head;
- tty->read_wait.wake_all();
- }
- } else {
- /* Raw / non-canonical mode */
- if (!buf_full(d)) {
- buf_put(d, c);
- echo_char(tty, c);
- tty->read_wait.wake_all();
- }
- }
- }
-}
-
-/* ================================================================== */
-/* N_TTY read — blocking read from the input buffer */
-/* ================================================================== */
-
-static int n_tty_read(tty_struct* tty, file* fp, char* buf, uint64 count)
-{
- auto* d = static_cast<n_tty_data*>(tty->ldisc.data);
- if (!d) return -EIO;
-
- const termios& t = tty->termios_data;
- TaskControlBlock* curr = current_task();
- int copied = 0;
-
- if (t.c_lflag & ICANON) {
- /* Canonical mode: block until a complete line is available */
- while (canon_count(d) == 0 && !d->eof_pending) {
- if (tty->flags & TTY_IO_ERROR)
- return -EIO;
-
- /* Check for pending signals — return -EINTR so the
- * trap return path can deliver them via do_signal() */
- if (signal_pending(curr))
- return -EINTR;
-
- tty->read_wait.prepare_to_wait(curr, TaskState::Sleeping);
- if (canon_count(d) == 0 && !d->eof_pending)
- schedule();
- tty->read_wait.finish_wait(curr);
- }
-
- if (d->eof_pending && canon_count(d) == 0) {
- d->eof_pending = false;
- return 0; /* EOF */
- }
-
- /* Copy up to one line */
- LockGuard guard(d->lock);
- while (copied < static_cast<int>(count) && d->read_tail != d->canon_head) {
- buf[copied++] = buf_get(d);
- }
- } else {
- /* Non-canonical mode */
- int vmin = t.c_cc[VMIN];
- /* VTIME not implemented (would need timers) */
-
- if (vmin == 0) vmin = 1;
-
- while (buf_count(d) < vmin) {
- if (tty->flags & TTY_IO_ERROR)
- return -EIO;
-
- if (signal_pending(curr))
- return -EINTR;
-
- tty->read_wait.prepare_to_wait(curr, TaskState::Sleeping);
- if (buf_count(d) < vmin)
- schedule();
- tty->read_wait.finish_wait(curr);
- }
-
- LockGuard guard(d->lock);
- while (copied < static_cast<int>(count) && buf_count(d) > 0) {
- buf[copied++] = buf_get(d);
- }
- }
-
- return copied;
-}
-
-/* ================================================================== */
-/* N_TTY write — output through the driver with post-processing */
-/* ================================================================== */
-
-static int n_tty_write(tty_struct* tty, file* fp, const char* buf, uint64 count)
-{
- if (!tty->driver || !tty->driver->ops || !tty->driver->ops->write)
- return -EIO;
-
- /* Apply output processing (OPOST) */
- if (tty->termios_data.c_oflag & OPOST) {
- for (uint64 i = 0; i < count; i++) {
- tty_output_char(tty, buf[i]);
- }
- return static_cast<int>(count);
- }
-
- /* Raw output: pass directly to driver */
- return tty->driver->ops->write(tty, buf, static_cast<int>(count));
-}
-
-/* ================================================================== */
-/* N_TTY ioctl */
-/* ================================================================== */
-
-static int n_tty_ioctl(tty_struct* tty, unsigned int cmd, uint64 arg)
-{
- auto* d = static_cast<n_tty_data*>(tty->ldisc.data);
-
- switch (cmd) {
- case FIONREAD: {
- int count = buf_count(d);
- auto* p = reinterpret_cast<int*>(arg);
- *p = count;
- return 0;
- }
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-/* ================================================================== */
-/* N_TTY open / close */
-/* ================================================================== */
-
-static int n_tty_open(tty_struct* tty)
-{
- auto* d = new n_tty_data{};
- if (!d) return -ENOMEM;
-
- d->read_head = 0;
- d->read_tail = 0;
- d->canon_head = 0;
- d->eof_pending = false;
- d->lock.init("n_tty");
-
- tty->ldisc.data = d;
- return 0;
-}
-
-static void n_tty_close(tty_struct* tty)
-{
- auto* d = static_cast<n_tty_data*>(tty->ldisc.data);
- if (d) {
- delete d;
- tty->ldisc.data = nullptr;
- }
-}
-
-static void n_tty_flush_buffer(tty_struct* tty)
-{
- auto* d = static_cast<n_tty_data*>(tty->ldisc.data);
- if (!d) return;
- LockGuard guard(d->lock);
- d->read_head = 0;
- d->read_tail = 0;
- d->canon_head = 0;
- d->eof_pending = false;
-}
-
-static int n_tty_chars_in_buffer(tty_struct* tty)
-{
- auto* d = static_cast<n_tty_data*>(tty->ldisc.data);
- if (!d) return 0;
- return buf_count(d);
-}
-
-/*
- * Probe input readiness for poll(). In canonical mode the read syscall
- * blocks until a whole line is queued, so we mirror that: POLLIN only
- * when canon_count() shows a finalized line (or pending EOF). In raw
- * mode any character satisfies VMIN==1 immediately. Without this
- * mirror, ppoll() would falsely signal POLLIN on partial canonical
- * input, then read() would block — exactly the busy-loop the shell's
- * ppoll-then-read pattern is trying to avoid.
- */
-bool n_tty_input_ready(tty_struct* tty)
-{
- auto* d = static_cast<n_tty_data*>(tty->ldisc.data);
- if (!d) return false;
- const termios& t = tty->termios_data;
- if (t.c_lflag & ICANON)
- return canon_count(d) != 0 || d->eof_pending;
- int vmin = t.c_cc[VMIN];
- if (vmin == 0) vmin = 1;
- return buf_count(d) >= vmin;
-}
-
-static void n_tty_set_termios(tty_struct* /*tty*/, const termios* /*old*/)
-{
- /* Nothing special needed for now */
-}
-
-static void n_tty_hangup(tty_struct* tty)
-{
- n_tty_flush_buffer(tty);
- tty->read_wait.wake_all();
-}
-
-/* ================================================================== */
-/* N_TTY ops table */
-/* ================================================================== */
-
-const tty_ldisc_ops n_tty_ops = {
- .name = "n_tty",
- .num = 0, /* N_TTY */
- .open = n_tty_open,
- .close = n_tty_close,
- .read = n_tty_read,
- .write = n_tty_write,
- .receive_buf = n_tty_receive_buf,
- .ioctl = n_tty_ioctl,
- .flush_buffer = n_tty_flush_buffer,
- .chars_in_buffer = n_tty_chars_in_buffer,
- .set_termios = n_tty_set_termios,
- .hangup = n_tty_hangup,
-};
diff --git a/na/drivers/tty/tty_core.cc b/na/drivers/tty/tty_core.cc
deleted file mode 100644
index 5985bd9..0000000
--- a/na/drivers/tty/tty_core.cc
+++ /dev/null
@@ -1,536 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * tty_core.cc — TTY subsystem core
- *
- * Provides the tty_struct lifecycle, file_operations dispatch,
- * and ioctl routing between the line discipline and driver.
- *
- * Call chain:
- * sys_read → tty_fops.read → tty_read → ldisc.read
- * sys_write → tty_fops.write → tty_write → ldisc.write
- * sys_ioctl → tty_fops.ioctl → tty_ioctl → {core / ldisc / driver}
- */
-
-#include <lume/tty.h>
-#include <lume/tty_ioctl.h>
-#include <lume/file.h>
-#include <lume/fs_types.h>
-#include <lume/chardev.h>
-#include <lume/signal.h>
-#include <lume/signal_types.h>
-#include <lume/signal_nr.h>
-#include <lume/task.h>
-#include <lume/pgrp.h>
-#include <lume/poll.h>
-#include <lume/new.h>
-#include <lume/errno.h>
-#include <lume/klog.h>
-#include <lume/slab.h>
-
-#ifndef ENOIOCTLCMD
-#define ENOIOCTLCMD 515
-#endif
-
-/* ================================================================== */
-/* Global console TTY */
-/* ================================================================== */
-
-tty_struct* g_console_tty = nullptr;
-
-/* ================================================================== */
-/* tty_struct allocation */
-/* ================================================================== */
-
-tty_struct* tty_alloc(const tty_driver* driver, int index)
-{
- auto* tty = new tty_struct{};
- if (!tty) return nullptr;
-
- tty->index = index;
- tty->driver = driver;
- tty->link = nullptr;
- tty->pgrp = 0;
- tty->session = 0;
- tty->write_cnt = 0;
- tty->flags = 0;
- tty->open_count = 0;
- tty->driver_data = nullptr;
-
- tty_default_termios(&tty->termios_data);
-
- tty->ws.ws_row = 24;
- tty->ws.ws_col = 80;
- tty->ws.ws_xpixel = 0;
- tty->ws.ws_ypixel = 0;
-
- tty->lock.init("tty");
- tty->read_wait.init("tty_rd");
- tty->write_wait.init("tty_wr");
-
- /* Attach default line discipline (N_TTY) */
- tty->ldisc.ops = &n_tty_ops;
- tty->ldisc.data = nullptr;
-
- return tty;
-}
-
-/* ================================================================== */
-/* tty_open / tty_close */
-/* ================================================================== */
-
-int tty_open(tty_struct* tty)
-{
- if (!tty) return -EINVAL;
-
- if (tty->open_count == 0) {
- /* Initialize line discipline */
- if (tty->ldisc.ops && tty->ldisc.ops->open) {
- int ret = tty->ldisc.ops->open(tty);
- if (ret < 0) return ret;
- }
-
- /* Initialize driver */
- if (tty->driver && tty->driver->ops && tty->driver->ops->open) {
- int ret = tty->driver->ops->open(tty);
- if (ret < 0) {
- if (tty->ldisc.ops && tty->ldisc.ops->close)
- tty->ldisc.ops->close(tty);
- return ret;
- }
- }
- }
-
- tty->open_count++;
- return 0;
-}
-
-void tty_close(tty_struct* tty)
-{
- if (!tty) return;
-
- tty->open_count--;
- if (tty->open_count <= 0) {
- tty->open_count = 0;
-
- if (tty->driver && tty->driver->ops && tty->driver->ops->close)
- tty->driver->ops->close(tty);
-
- if (tty->ldisc.ops && tty->ldisc.ops->close)
- tty->ldisc.ops->close(tty);
- }
-}
-
-/* ================================================================== */
-/* Core I/O */
-/* ================================================================== */
-
-int64 tty_read(tty_struct* tty, file* fp, char* buf, uint64 count)
-{
- if (!tty || !tty->ldisc.ops || !tty->ldisc.ops->read)
- return -EIO;
-
- return tty->ldisc.ops->read(tty, fp, buf, count);
-}
-
-int64 tty_write(tty_struct* tty, file* fp, const char* buf, uint64 count)
-{
- if (!tty || !tty->ldisc.ops || !tty->ldisc.ops->write)
- return -EIO;
-
- return tty->ldisc.ops->write(tty, fp, buf, count);
-}
-
-/* ================================================================== */
-/* tty_receive_buf — called by driver on RX */
-/* ================================================================== */
-
-void tty_receive_buf(tty_struct* tty, const char* buf, int count)
-{
- if (!tty || !tty->ldisc.ops || !tty->ldisc.ops->receive_buf)
- return;
-
- tty->ldisc.ops->receive_buf(tty, buf, count);
-}
-
-/* ================================================================== */
-/* tty_signal_pgrp — send signal to foreground process group */
-/* ================================================================== */
-
-extern int send_signal(int sig, kernel_siginfo* info, TaskControlBlock* task);
-
-void tty_signal_pgrp(tty_struct* tty, int sig)
-{
- if (!tty) return;
-
- int32 pgid;
- {
- LockGuard guard(tty->lock);
- pgid = tty->pgrp;
- }
- if (pgid <= 0) return;
-
- /* Deliver to the leader of every thread group in the foreground
- * process group. ldisc paths call this for SIGINT/SIGQUIT/SIGWINCH
- * etc.; previously we only signalled the current task, which was
- * incorrect when the fg pgrp had multiple processes. */
- kernel_siginfo info{};
- info.si_signo = sig;
- info.si_code = 0; /* SI_KERNEL would be 0x80; user rarely checks */
- for_each_pgrp_member(pgid, [&](signal_struct* s) {
- if (s->leader)
- send_signal(sig, &info, s->leader);
- });
-}
-
-/* ================================================================== */
-/* tty_ioctl — ioctl dispatch */
-/* ================================================================== */
-
-long tty_ioctl(tty_struct* tty, file* /*fp*/, unsigned int cmd, uint64 arg)
-{
- if (!tty) return -EINVAL;
-
- switch (cmd) {
- case TCGETS: {
- auto* p = reinterpret_cast<termios*>(arg);
- if (!p) return -EINVAL;
- *p = tty->termios_data;
- return 0;
- }
-
- case TCSETS:
- case TCSETSW:
- case TCSETSF: {
- auto* p = reinterpret_cast<const termios*>(arg);
- if (!p) return -EINVAL;
- termios old = tty->termios_data;
- tty->termios_data = *p;
-
- /* Notify line discipline of termios change */
- if (tty->ldisc.ops && tty->ldisc.ops->set_termios)
- tty->ldisc.ops->set_termios(tty, &old);
-
- /* If TCSETSF, flush input buffer */
- if (cmd == TCSETSF) {
- if (tty->ldisc.ops && tty->ldisc.ops->flush_buffer)
- tty->ldisc.ops->flush_buffer(tty);
- }
- return 0;
- }
-
- case TIOCGWINSZ: {
- auto* p = reinterpret_cast<winsize*>(arg);
- if (!p) return -EINVAL;
- *p = tty->ws;
- return 0;
- }
-
- case TIOCSWINSZ: {
- auto* p = reinterpret_cast<const winsize*>(arg);
- if (!p) return -EINVAL;
- winsize old = tty->ws;
- tty->ws = *p;
- if (old.ws_row != tty->ws.ws_row || old.ws_col != tty->ws.ws_col) {
- tty_signal_pgrp(tty, SIGWINCH);
- }
- return 0;
- }
-
- case TIOCGPGRP: {
- auto* p = reinterpret_cast<int32*>(arg);
- if (!p) return -EINVAL;
-
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return -ENOTTY;
- if (curr->signal->tty != tty) return -ENOTTY;
-
- int32 pgid;
- {
- LockGuard guard(tty->lock);
- pgid = tty->pgrp != 0 ? tty->pgrp : curr->signal->pgid;
- }
- *p = pgid;
- return 0;
- }
-
- case TIOCSPGRP: {
- auto* p = reinterpret_cast<const int32*>(arg);
- if (!p) return -EINVAL;
-
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return -ENOTTY;
- if (curr->signal->tty != tty) return -ENOTTY;
-
- int32 new_pgid = *p;
- if (new_pgid <= 0) return -EINVAL;
-
- /* new_pgid must be in the caller's session. */
- bool ok = false;
- for_each_pgrp_member(new_pgid, [&](signal_struct* s) {
- if (s->sid == curr->signal->sid) ok = true;
- });
- if (!ok) return -EPERM;
-
- LockGuard guard(tty->lock);
- tty->pgrp = new_pgid;
- return 0;
- }
-
- case TIOCSCTTY: {
- /* Become the controlling terminal of the caller's session.
- * Linux: only session leader without ctty may do this; with
- * arg==1, CAP_SYS_ADMIN may steal a ctty from another session.
- * We support the basic path; "steal" is left unimplemented. */
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return -EPERM;
- signal_struct* sig = curr->signal;
-
- if (sig->sid != curr->pid) return -EPERM; /* not leader */
-
- LockGuard sg(sig->lock);
- if (sig->tty != nullptr) return -EPERM; /* already have one */
-
- LockGuard tg(tty->lock);
- if (tty->session != 0 && tty->session != sig->sid) {
- return -EPERM; /* owned elsewhere */
- }
- tty->session = sig->sid;
- tty->pgrp = sig->pgid;
- sig->tty = tty;
- return 0;
- }
-
- case TIOCNOTTY: {
- /* Detach from controlling terminal. */
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return -ENOTTY;
- signal_struct* sig = curr->signal;
- if (sig->tty != tty) return -ENOTTY;
-
- /* Session leader: HUP everyone left in the old fg pgrp.
- * Job-control conformance — without this, daemons spawned by
- * a relinquishing leader keep blocking on a dead terminal. */
- bool was_leader = (sig->sid == curr->pid);
- int32 hup_pgrp = 0;
- {
- LockGuard tg(tty->lock);
- hup_pgrp = tty->pgrp;
- if (was_leader) {
- tty->session = 0;
- tty->pgrp = 0;
- }
- }
- {
- LockGuard sg(sig->lock);
- sig->tty = nullptr;
- }
- if (was_leader && hup_pgrp > 0) {
- tty_signal_pgrp(tty, SIGHUP);
- tty_signal_pgrp(tty, SIGCONT);
- (void)hup_pgrp;
- }
- return 0;
- }
-
- case FIONREAD: {
- /* Delegate to line discipline */
- if (tty->ldisc.ops && tty->ldisc.ops->ioctl) {
- int ret = tty->ldisc.ops->ioctl(tty, cmd, arg);
- if (ret != -ENOIOCTLCMD)
- return ret;
- }
- return 0;
- }
-
- case TCFLSH: {
- int queue = static_cast<int>(arg);
- if (queue == 0 || queue == 2) {
- /* Flush input */
- if (tty->ldisc.ops && tty->ldisc.ops->flush_buffer)
- tty->ldisc.ops->flush_buffer(tty);
- }
- if (queue == 1 || queue == 2) {
- /* Flush output */
- if (tty->driver && tty->driver->ops && tty->driver->ops->flush_buffer)
- tty->driver->ops->flush_buffer(tty);
- }
- return 0;
- }
-
- default:
- break;
- }
-
- /* Try line discipline ioctl */
- if (tty->ldisc.ops && tty->ldisc.ops->ioctl) {
- int ret = tty->ldisc.ops->ioctl(tty, cmd, arg);
- if (ret != -ENOIOCTLCMD)
- return ret;
- }
-
- /* Try driver ioctl */
- if (tty->driver && tty->driver->ops && tty->driver->ops->ioctl) {
- int ret = tty->driver->ops->ioctl(tty, cmd, arg);
- if (ret != -ENOIOCTLCMD)
- return ret;
- }
-
- return -ENOTTY;
-}
-
-/* ================================================================== */
-/* file_operations for TTY devices */
-/* ================================================================== */
-
-static int64 tty_fops_read(file* fp, char* buf, uint64 count, int64* /*pos*/)
-{
- auto* tty = static_cast<tty_struct*>(fp->private_data);
- return tty_read(tty, fp, buf, count);
-}
-
-static int64 tty_fops_write(file* fp, const char* buf, uint64 count, int64* /*pos*/)
-{
- auto* tty = static_cast<tty_struct*>(fp->private_data);
- return tty_write(tty, fp, buf, count);
-}
-
-static long tty_fops_ioctl(file* fp, unsigned int cmd, uint64 arg)
-{
- auto* tty = static_cast<tty_struct*>(fp->private_data);
- return tty_ioctl(tty, fp, cmd, arg);
-}
-
-/*
- * tty poll callback — Linux drivers/tty/tty_io.c:tty_poll() does the
- * same thing: register tty->read_wait so the poller is woken on input,
- * then ask the ldisc whether data is currently available. Write side
- * is always considered ready since our writes don't block.
- */
-extern bool n_tty_input_ready(tty_struct* tty);
-extern void poll_wait(file*, WaitQueue*, poll_table*);
-
-static uint32 tty_fops_poll(file* fp, void* pt)
-{
- auto* tty = static_cast<tty_struct*>(fp->private_data);
- if (!tty) return POLLNVAL;
-
- poll_wait(fp, &tty->read_wait, static_cast<poll_table*>(pt));
-
- uint32 mask = POLLOUT | POLLWRNORM; /* always writable */
- if (n_tty_input_ready(tty))
- mask |= POLLIN | POLLRDNORM;
- return mask;
-}
-
-/*
- * Auto-acquire a controlling terminal on open.
- *
- * POSIX rule: when a session leader without a controlling tty opens a
- * terminal that itself has no controlling session, and the caller did
- * not pass O_NOCTTY, that terminal becomes the controlling terminal of
- * the caller's session. tty->session and tty->pgrp are seeded from the
- * caller's sid/pgid so that the very first tcgetpgrp() call returns a
- * value matching the caller's pgid — which is precisely what BusyBox
- * ash:setjobctl() needs to exit its setup loop.
- *
- * Reference: Linux drivers/tty/tty_jobctrl.c:tty_open_proc_set_tty()
- */
-static void tty_acquire_ctty(tty_struct* tty, file* fp)
-{
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return;
- if (fp->f_flags & O_NOCTTY) return;
-
- signal_struct* sig = curr->signal;
-
- /* Must be session leader (sid == pid). */
- if (sig->sid != curr->pid) return;
-
- LockGuard sig_guard(sig->lock);
- if (sig->tty != nullptr) return; /* already have one */
-
- LockGuard tty_guard(tty->lock);
- if (tty->session != 0) return; /* tty owned by another sess */
-
- tty->session = sig->sid;
- tty->pgrp = sig->pgid;
- sig->tty = tty;
-}
-
-static int tty_fops_open(inode* /*ip*/, file* fp)
-{
- /* The console TTY is pre-allocated; just attach it */
- if (!g_console_tty) return -ENXIO;
- fp->private_data = g_console_tty;
- int err = tty_open(g_console_tty);
- if (err == 0) {
- tty_acquire_ctty(g_console_tty, fp);
- }
- return err;
-}
-
-static int tty_fops_release(inode* /*ip*/, file* fp)
-{
- auto* tty = static_cast<tty_struct*>(fp->private_data);
- tty_close(tty);
- return 0;
-}
-
-file_operations tty_fops;
-
-static void init_tty_fops()
-{
- tty_fops.read = tty_fops_read;
- tty_fops.write = tty_fops_write;
- tty_fops.unlocked_ioctl = tty_fops_ioctl;
- tty_fops.poll = tty_fops_poll;
- tty_fops.open = tty_fops_open;
- tty_fops.release = tty_fops_release;
-}
-
-/* ================================================================== */
-/* TTY chardev registration */
-/* ================================================================== */
-
-static cdev tty_cdev;
-
-void tty_init()
-{
- init_tty_fops();
-
- /* Allocate the console TTY */
- g_console_tty = tty_alloc(&uart_tty_driver, 0);
- if (!g_console_tty) {
- return;
- }
-
- /* Open the TTY (initializes ldisc + driver) */
- tty_open(g_console_tty);
-
- /* Leave tty->session and tty->pgrp at 0. The first session-leader
- * to open /dev/console without O_NOCTTY claims it as its controlling
- * terminal via tty_acquire_ctty(), which fills both fields. Pre-
- * seeding them here (as we used to with the hardcoded pid==1) would
- * make every subsequent acquire path see "owned by another session"
- * and bail out, leaving sh's tcgetpgrp() returning -ENOTTY and
- * disabling job control. */
-
- /* Initialize UART RX interrupts */
- uart_tty_init(g_console_tty);
-
- /* Register UART as a struct console driver.
- * After this, printk/klog output goes through console_write_all()
- * → uart_console_write() instead of raw early_putc(). */
- extern void uart_register_console();
- uart_register_console();
-
- /* Register as /dev/console chardev — replace the old console_cdev.
- * We use the same major/minor (5,1) so that existing opens
- * to /dev/console will get the TTY fops. */
- tty_cdev.name = "console";
- tty_cdev.dev = MKDEV(5, 1);
- tty_cdev.fops = &tty_fops;
- cdev_register(&tty_cdev);
-}
diff --git a/na/drivers/tty/tty_uart.cc b/na/drivers/tty/tty_uart.cc
deleted file mode 100644
index 973a6e9..0000000
--- a/na/drivers/tty/tty_uart.cc
+++ /dev/null
@@ -1,251 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * tty_uart.cc — UART → tty_driver adapter
- *
- * Bridges the 16550A UART hardware to the TTY subsystem.
- *
- * TX: Calls early_putc() for each character (same as before).
- * RX: Registers a PLIC IRQ handler. On UART RX interrupt,
- * reads available bytes and feeds them to the line discipline
- * via tty_receive_buf().
- *
- * QEMU virt UART:
- * - IRQ 10 on the PLIC
- * - 16550A register layout at the base address discovered from FDT
- */
-
-#include <lume/tty.h>
-#include <lume/irq.h>
-#include <lume/addr.h>
-#include <lume/fdt.h>
-#include <lume/vmm.h>
-#include <lume/errno.h>
-#include <lume/klog.h>
-
-#ifndef ENOIOCTLCMD
-#define ENOIOCTLCMD 515
-#endif
-
-extern "C" void early_putc(char c);
-
-/* 16550A register offsets */
-static constexpr int UART_RBR = 0; /* Receive Buffer Register */
-static constexpr int UART_THR = 0; /* Transmit Holding Register */
-static constexpr int UART_IER = 1; /* Interrupt Enable Register */
-static constexpr int UART_IIR = 2; /* Interrupt Identification Register */
-static constexpr int UART_FCR = 2; /* FIFO Control Register */
-static constexpr int UART_LCR = 3; /* Line Control Register */
-static constexpr int UART_MCR = 4; /* Modem Control Register */
-static constexpr int UART_LSR = 5; /* Line Status Register */
-
-/* LSR bits */
-static constexpr uint8 UART_LSR_DR = 0x01; /* Data Ready */
-static constexpr uint8 UART_LSR_THRE = 0x20; /* TX Holding Register Empty */
-
-/* IER bits */
-static constexpr uint8 UART_IER_RDI = 0x01; /* Received Data Available */
-
-/* MCR bits */
-static constexpr uint8 UART_MCR_DTR = 0x01; /* Data Terminal Ready */
-static constexpr uint8 UART_MCR_RTS = 0x02; /* Request To Send */
-static constexpr uint8 UART_MCR_OUT2 = 0x08; /* Auxiliary output 2 — gates the
- IRQ pin on PC-style 16550A.
- Without this the UART
- internally asserts but does
- NOT propagate to the PLIC. */
-
-/* QEMU virt default UART IRQ */
-static constexpr uint32 UART_IRQ = 10;
-
-/* UART MMIO base (virtual address) */
-static volatile uint8* g_uart_base;
-static tty_struct* g_uart_tty;
-
-/* ================================================================== */
-/* UART MMIO helpers */
-/* ================================================================== */
-
-static inline uint8 uart_read(int reg)
-{
- return g_uart_base[reg];
-}
-
-static inline void uart_write(int reg, uint8 val)
-{
- g_uart_base[reg] = val;
-}
-
-static inline bool uart_rx_ready()
-{
- return (uart_read(UART_LSR) & UART_LSR_DR) != 0;
-}
-
-static inline char uart_rx_char()
-{
- return static_cast<char>(uart_read(UART_RBR));
-}
-
-/* ================================================================== */
-/* UART RX interrupt handler */
-/* ================================================================== */
-
-static void uart_irq_handler(uint32 /*irq*/, void* arg)
-{
- auto* tty = static_cast<tty_struct*>(arg);
-
- /* Read all available bytes from the FIFO */
- char buf[16];
- int count = 0;
-
- while (uart_rx_ready() && count < 16) {
- buf[count++] = uart_rx_char();
- }
-
- if (count > 0) {
- tty_receive_buf(tty, buf, count);
- }
-}
-
-/* ================================================================== */
-/* tty_driver_ops for UART */
-/* ================================================================== */
-
-static int uart_tty_open_op(tty_struct* /*tty*/)
-{
- return 0;
-}
-
-static void uart_tty_close_op(tty_struct* /*tty*/)
-{
- /* Nothing to do */
-}
-
-static int uart_tty_write_op(tty_struct* /*tty*/, const char* buf, int count)
-{
- for (int i = 0; i < count; i++) {
- early_putc(buf[i]);
- }
- return count;
-}
-
-static int uart_tty_write_room_op(tty_struct* /*tty*/)
-{
- return TTY_BUF_SIZE; /* Always room (blocking putc) */
-}
-
-static void uart_tty_put_char_op(tty_struct* /*tty*/, char ch)
-{
- early_putc(ch);
-}
-
-static void uart_tty_flush_chars_op(tty_struct* /*tty*/)
-{
- /* No buffering in early_putc, nothing to flush */
-}
-
-static int uart_tty_chars_in_buffer_op(tty_struct* /*tty*/)
-{
- return 0; /* No TX buffer */
-}
-
-static void uart_tty_flush_buffer_op(tty_struct* /*tty*/)
-{
- /* No TX buffer to flush */
-}
-
-static int uart_tty_ioctl_op(tty_struct* /*tty*/, unsigned int /*cmd*/, uint64 /*arg*/)
-{
- return -ENOIOCTLCMD;
-}
-
-static void uart_tty_hangup_op(tty_struct* /*tty*/)
-{
- /* Disable UART RX interrupts */
- if (g_uart_base) {
- uart_write(UART_IER, 0);
- }
-}
-
-static const tty_driver_ops uart_ops = {
- .open = uart_tty_open_op,
- .close = uart_tty_close_op,
- .write = uart_tty_write_op,
- .write_room = uart_tty_write_room_op,
- .put_char = uart_tty_put_char_op,
- .flush_chars = uart_tty_flush_chars_op,
- .chars_in_buffer = uart_tty_chars_in_buffer_op,
- .flush_buffer = uart_tty_flush_buffer_op,
- .ioctl = uart_tty_ioctl_op,
- .throttle = nullptr,
- .unthrottle = nullptr,
- .hangup = uart_tty_hangup_op,
-};
-
-const tty_driver uart_tty_driver = {
- .name = "ttyS",
- .major = 4,
- .minor_start = 64,
- .num = 1,
- .ops = &uart_ops,
-};
-
-/* ================================================================== */
-/* UART TTY init — discover base address, enable RX IRQ */
-/* ================================================================== */
-
-void uart_tty_init(tty_struct* tty)
-{
- g_uart_tty = tty;
-
- /* Discover UART base from FDT */
- DeviceNode* node = fdt_find_compatible("ns16550a");
- if (!node) {
- /* Try alternative compatible string */
- node = fdt_find_compatible("snps,dw-apb-uart");
- }
-
- uint64 uart_pa = 0;
- if (node) {
- uart_pa = node->get_prop_u64("reg", 0);
- }
-
- if (uart_pa == 0) {
- /* Fallback: QEMU virt default */
- uart_pa = 0x10000000;
- }
-
- g_uart_base = reinterpret_cast<volatile uint8*>(
- pa_to_va(phys_addr(uart_pa)).raw);
-
- /* Set FIFO control: enable FIFOs, clear RX/TX FIFOs, 1-byte trigger */
- uart_write(UART_FCR, 0x07);
-
- /* MCR: assert OUT2 (gates the IRQ pin on PC-style 16550A), plus
- * DTR/RTS for completeness. Mirrors Linux drivers/tty/serial/8250/
- * 8250_port.c: serial8250_do_startup() does the same. */
- uart_write(UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
-
- /* Enable UART receive-data-available interrupt */
- uart_write(UART_IER, UART_IER_RDI);
-
- /* Register IRQ handler with PLIC */
- uint32 irq = UART_IRQ;
- if (node) {
- /* Try to get IRQ number from FDT */
- const FdtProperty* irq_prop = node->get_prop("interrupts");
- if (irq_prop && irq_prop->len >= 4) {
- const uint8* val = static_cast<const uint8*>(irq_prop->value);
- irq = (uint32(val[0]) << 24) | (uint32(val[1]) << 16) |
- (uint32(val[2]) << 8) | val[3];
- }
- }
-
- g_irq_manager.register_handler(irq, uart_irq_handler, tty);
- g_irq_manager.enable(irq);
-
- printk(KERN_INFO "uart_tty_init: base=0x%llx irq=%u IER=0x%x LSR=0x%x\n",
- (unsigned long long)uart_pa, irq,
- uart_read(UART_IER), uart_read(UART_LSR));
-}
diff --git a/na/drivers/uart.cc b/na/drivers/uart.cc
deleted file mode 100644
index 467b496..0000000
--- a/na/drivers/uart.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * uart.cc — 16550a UART driver + console registry
- *
- * Two roles:
- * 1. Early console: early_putc() for pre-init output (panic, kprintf)
- * 2. Runtime console: struct console registered with register_console().
- * klog/printk route through console_write_all() → uart_console_write().
- *
- * The console registry is a simple linked list of struct console.
- * console_write_all() iterates the list and calls each console's write().
- * If no console is registered, it falls back to early_putc().
- *
- * Reference: Linux kernel/printk/printk.c, drivers/tty/serial/8250/
- */
-
-#include <lume/console.h>
-#include <lume/fdt.h>
-#include <lume/vmm.h>
-#include <lume/addr.h>
-#include <arch/config.h>
-
-/* ================================================================== */
-/* UART state */
-/* ================================================================== */
-
-static uint64 g_uart_pa = 0;
-static uint64 g_uart_size = 0;
-static bool g_runtime_mapping_ready = false;
-static bool g_mmu_enabled = false;
-
-/* ================================================================== */
-/* Early console (Phase 1) */
-/* ================================================================== */
-
-extern "C" void console_set_mmu_enabled() {
- g_mmu_enabled = true;
-}
-
-extern "C" void early_putc(char c) {
- if (!g_uart_pa) return;
-
- /*
- * Before vmm_init() activates the final page table, UART MMIO is only
- * reachable via the bootstrap fixed direct map from entry.S.
- * After activation, switch to the runtime (possibly KASLR-slid) base.
- */
- uint64 uart_va = 0;
- if (g_runtime_mapping_ready) {
- uart_va = pa_to_va(phys_addr(g_uart_pa)).raw;
- } else if (!g_mmu_enabled) {
- uart_va = g_uart_pa;
- } else {
- uart_va = boot_pa_to_va(phys_addr(g_uart_pa)).raw;
- }
-
- volatile auto* uart = reinterpret_cast<volatile uint8*>(uart_va);
- *uart = static_cast<uint8>(c);
-}
-
-extern "C" void early_puts(const char* s) {
- while (*s) early_putc(*s++);
-}
-
-void console_early_init(uint64 fdt_paddr) {
- uint64 pa = 0;
- uint64 size = 0;
- FdtManager::early_scan_uart(fdt_paddr, &pa, &size);
- if (pa != 0) {
- g_uart_pa = pa;
- if (size != 0) g_uart_size = size;
- }
-}
-
-void console_init() {
- /*
- * Once vmm_init() activates the new page table, the 1GB identity map is dropped.
- * The UART must be explicitly registered into the active page table.
- */
- if (g_uart_pa != 0 && g_uart_size != 0) {
- vmm_map_kernel_mmio(g_uart_pa, g_uart_size);
- }
-}
-
-void console_use_runtime_mapping() {
- g_runtime_mapping_ready = true;
-}
-
-/* ================================================================== */
-/* Console registry (Phase 2) — linked list of struct console */
-/* ================================================================== */
-
-static list_node g_console_list;
-static bool g_console_list_init = false;
-
-void register_console(console* con)
-{
- if (!con) return;
-
- if (!g_console_list_init) {
- g_console_list.init();
- g_console_list_init = true;
- }
-
- con->flags |= CON_ENABLED;
- con->list.init();
-
- /* Insert at tail */
- list_add_tail(&con->list, &g_console_list);
-}
-
-void unregister_console(console* con)
-{
- if (!con) return;
-
- list_del_init(&con->list);
- con->flags &= ~CON_ENABLED;
-}
-
-bool console_drivers_registered()
-{
- if (!g_console_list_init) return false;
- return !list_empty(&g_console_list);
-}
-
-void console_write_all(const char* s, int count)
-{
- if (g_console_list_init && !list_empty(&g_console_list)) {
- list_node* pos;
- list_for_each(pos, &g_console_list) {
- auto* con = container_of(pos, console, list);
- if ((con->flags & CON_ENABLED) && con->write) {
- con->write(con, s, static_cast<unsigned int>(count));
- }
- }
- } else {
- /* Fallback: direct early_putc (pre-registration) */
- for (int i = 0; i < count; i++) {
- early_putc(s[i]);
- }
- }
-}
-
-/* ================================================================== */
-/* UART console driver */
-/* ================================================================== */
-
-static void uart_console_write(console* /*con*/, const char* s, unsigned int count)
-{
- for (unsigned int i = 0; i < count; i++) {
- if (s[i] == '\n') {
- early_putc('\r');
- }
- early_putc(s[i]);
- }
-}
-
-/*
- * uart_console must be initialized at RUNTIME, not with static
- * aggregate initializers. Reason: KASLR self-relocation processes
- * .rela.dyn entries for simple pointer variables, but may not cover
- * function pointer fields inside static aggregate initializers.
- * A static `{ .write = uart_console_write }` would store the
- * pre-relocation address, causing a crash when called.
- */
-static console uart_console;
-
-void uart_register_console()
-{
- uart_console.name = "ttyS";
- uart_console.write = uart_console_write;
- uart_console.setup = nullptr;
- uart_console.flags = CON_ENABLED | CON_BOOT;
- uart_console.index = 0;
- uart_console.data = nullptr;
- uart_console.list.init();
- register_console(&uart_console);
-}
-
diff --git a/na/fdt/device_node.cc b/na/fdt/device_node.cc
deleted file mode 100644
index 0c8ad46..0000000
--- a/na/fdt/device_node.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * device_node.cc — DeviceNode implementation
- *
- * Provides property lookup and child traversal for the unflattened
- * device tree. After construction, the tree is globally read-only.
- *
- * Reference: docs/specs/fdt.md §3.2
- */
-
-#include <lume/device_node.h>
-
-/* Minimal strcmp — no lib dependency */
-static bool str_eq(const char *a, const char *b)
-{
- while (*a && *a == *b) { a++; b++; }
- return *a == *b;
-}
-
-/* ------------------------------------------------------------------ */
-/* Construction / Destruction */
-/* ------------------------------------------------------------------ */
-
-DeviceNode::DeviceNode(DeviceNode *parent, const char *name)
- : name_(name), parent_(parent)
-{
- children_list_.init();
- props_list_.init();
- sibling_link_.init();
-}
-
-DeviceNode::~DeviceNode()
-{
- /* Recursively delete children */
- list_node *cur = children_list_.next;
- while (cur != &children_list_) {
- list_node *next = cur->next;
- /* Recover DeviceNode* from sibling_link_ offset */
- DeviceNode *child = list_entry<DeviceNode, &DeviceNode::sibling_link_>(cur);
- delete child;
- cur = next;
- }
-
- /* Delete properties */
- list_node *pc = props_list_.next;
- while (pc != &props_list_) {
- list_node *pn = pc->next;
- FdtProperty *prop = reinterpret_cast<FdtProperty *>(
- reinterpret_cast<uintptr>(pc) -
- __builtin_offsetof(FdtProperty, link));
- delete prop;
- pc = pn;
- }
-}
-
-/* ------------------------------------------------------------------ */
-/* Tree construction (used by FdtManager::unflatten) */
-/* ------------------------------------------------------------------ */
-
-void DeviceNode::add_child(DeviceNode *child)
-{
- /* Append to end of children list */
- list_node *node = &child->sibling_link_;
- node->prev = children_list_.prev;
- node->next = &children_list_;
- children_list_.prev->next = node;
- children_list_.prev = node;
-}
-
-void DeviceNode::add_prop(FdtProperty *prop)
-{
- list_node *node = &prop->link;
- node->prev = props_list_.prev;
- node->next = &props_list_;
- props_list_.prev->next = node;
- props_list_.prev = node;
-}
-
-/* ------------------------------------------------------------------ */
-/* Property access */
-/* ------------------------------------------------------------------ */
-
-const FdtProperty *DeviceNode::get_prop(const char *name) const
-{
- const list_node *cur = props_list_.next;
- while (cur != &props_list_) {
- const FdtProperty *prop = reinterpret_cast<const FdtProperty *>(
- reinterpret_cast<uintptr>(cur) -
- __builtin_offsetof(FdtProperty, link));
- if (str_eq(prop->name, name))
- return prop;
- cur = cur->next;
- }
- return nullptr;
-}
-
-/* Big-endian helpers for decoding property values */
-static inline uint32 be32(const void *p)
-{
- const uint8 *b = static_cast<const uint8 *>(p);
- return (uint32(b[0]) << 24) | (uint32(b[1]) << 16) |
- (uint32(b[2]) << 8) | uint32(b[3]);
-}
-
-static inline uint64 be64(const void *p)
-{
- const uint8 *b = static_cast<const uint8 *>(p);
- return (uint64(be32(b)) << 32) | uint64(be32(b + 4));
-}
-
-uint32 DeviceNode::get_prop_u32(const char *name, uint32 default_val) const
-{
- const FdtProperty *p = get_prop(name);
- if (!p || p->len < 4)
- return default_val;
- return be32(p->value);
-}
-
-uint64 DeviceNode::get_prop_u64(const char *name, uint64 default_val) const
-{
- const FdtProperty *p = get_prop(name);
- if (!p)
- return default_val;
- if (p->len >= 8)
- return be64(p->value);
- if (p->len >= 4)
- return be32(p->value);
- return default_val;
-}
-
-const char *DeviceNode::get_prop_string(const char *name) const
-{
- const FdtProperty *p = get_prop(name);
- if (!p || p->len == 0)
- return nullptr;
- return static_cast<const char *>(p->value);
-}
-
-/* ------------------------------------------------------------------ */
-/* Child traversal */
-/* ------------------------------------------------------------------ */
-
-DeviceNode *DeviceNode::find_child(const char *name)
-{
- list_node *cur = children_list_.next;
- while (cur != &children_list_) {
- DeviceNode *child = list_entry<DeviceNode, &DeviceNode::sibling_link_>(cur);
- if (str_eq(child->name_, name))
- return child;
- cur = cur->next;
- }
- return nullptr;
-}
-
-void DeviceNode::for_each_child(void (*callback)(DeviceNode *))
-{
- list_node *cur = children_list_.next;
- while (cur != &children_list_) {
- list_node *next = cur->next;
- DeviceNode *child = list_entry<DeviceNode, &DeviceNode::sibling_link_>(cur);
- callback(child);
- cur = next;
- }
-}
diff --git a/na/fdt/fdt.cc b/na/fdt/fdt.cc
deleted file mode 100644
index d44ebda..0000000
--- a/na/fdt/fdt.cc
+++ /dev/null
@@ -1,625 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * fdt.cc — Flattened Device Tree parser implementation
- *
- * Implements the two-phase FDT parsing strategy described in fdt.md:
- *
- * Phase 1 (Early Scan):
- * Static methods that linearly walk the raw FDT blob in-place,
- * performing zero memory allocation. Called before PMM/Slab exist.
- *
- * Phase 2 (Unflatten):
- * Builds a persistent DeviceNode C++ object tree using Slab.
- * Stub implementation — will be completed when Slab is available.
- *
- * Reference: docs/specs/fdt.md
- */
-
-#include <lume/addr.h>
-#include <lume/fdt.h>
-#include <lume/klog.h>
-#include <lume/new.h>
-
-/* ------------------------------------------------------------------ */
-/* Global singleton (Placement New pattern, see global_init.md §3.1) */
-/* ------------------------------------------------------------------ */
-
-alignas(FdtManager) uint8 g_fdt_buf[sizeof(FdtManager)];
-FdtManager *g_fdt = nullptr;
-
-/* ------------------------------------------------------------------ */
-/* FDT binary format constants (DTSpec v0.4) */
-/* ------------------------------------------------------------------ */
-
-struct fdt_header {
- uint32 magic;
- uint32 totalsize;
- uint32 off_dt_struct;
- uint32 off_dt_strings;
- uint32 off_mem_rsvmap;
- uint32 version;
- uint32 last_comp_version;
- uint32 boot_cpuid_phys;
- uint32 size_dt_strings;
- uint32 size_dt_struct;
-};
-
-#define FDT_MAGIC 0xd00dfeed
-#define FDT_BEGIN_NODE 1
-#define FDT_END_NODE 2
-#define FDT_PROP 3
-#define FDT_NOP 4
-#define FDT_END 9
-
-/* ------------------------------------------------------------------ */
-/* Minimal string helpers (no lib/ dependency for early boot) */
-/* ------------------------------------------------------------------ */
-
-static inline bool str_starts_with(const char *str, const char *prefix) {
- while (*prefix) {
- if (*str++ != *prefix++)
- return false;
- }
- return true;
-}
-
-static inline bool str_equals(const char *s1, const char *s2) {
- while (*s1 && *s1 == *s2) {
- s1++;
- s2++;
- }
- return *s1 == *s2;
-}
-
-/*
- * Check whether a null-separated string list (e.g. the "compatible"
- * property) contains @target.
- */
-static bool prop_contains_string(const char *prop_val, uint32 len,
- const char *target) {
- uint32 i = 0;
- while (i < len) {
- const char *s = prop_val + i;
- if (str_equals(s, target))
- return true;
- while (i < len && prop_val[i] != '\0')
- i++;
- i++; // skip '\0' separator
- }
- return false;
-}
-
-/* ------------------------------------------------------------------ */
-/* Byte-swap helpers */
-/* ------------------------------------------------------------------ */
-
-/*
- * Manual 32-bit big-endian to little-endian conversion.
- * We cannot use __builtin_bswap32 because on RISC-V without the Zbb
- * extension the compiler emits a call to libgcc's __bswapsi2, which
- * is unavailable in a freestanding (-nostdlib) environment.
- */
-static inline uint32 bswap32(uint32 x) {
- return ((x & 0xFF000000u) >> 24) | ((x & 0x00FF0000u) >> 8) |
- ((x & 0x0000FF00u) << 8) | ((x & 0x000000FFu) << 24);
-}
-
-/*
- * Read a big-endian cell value from the FDT struct block.
- * @cells: number of 32-bit cells (1 or 2).
- * 2-cell values are common for 64-bit addresses/sizes.
- */
-static inline uint64 read_cells(const uint8 *data, uint32 cells) {
- const uint32 *p = reinterpret_cast<const uint32 *>(data);
- if (cells == 2) {
- uint64 high = bswap32(p[0]);
- uint64 low = bswap32(p[1]);
- return (high << 32) | low;
- }
- return bswap32(p[0]);
-}
-
-/* ------------------------------------------------------------------ */
-/* FdtManager construction */
-/* ------------------------------------------------------------------ */
-
-FdtManager::FdtManager(uint64 fdt_paddr)
- : fdt_paddr_(fdt_paddr) {}
-
-/* ------------------------------------------------------------------ */
-/* Phase 1: Early scan — memory layout */
-/* ------------------------------------------------------------------ */
-
-/*
- * Walk the raw FDT struct block to locate the /memory node and
- * extract its "reg" property (base address + size).
- *
- * Cell sizes (#address-cells, #size-cells) are tracked per depth
- * level to correctly decode the "reg" value under any bus topology.
- *
- * Panics on invalid magic or missing /memory node — there is no
- * recovery path this early in boot.
- */
-void FdtManager::early_scan_mem(uint64 fdt_paddr, uint64 *base, uint64 *size) {
- const fdt_header *hdr = reinterpret_cast<const fdt_header *>(fdt_paddr);
- if (bswap32(hdr->magic) != FDT_MAGIC)
- kernel_panic("FDT early_scan_mem: invalid magic number", nullptr);
-
- const uint8 *dt_struct =
- reinterpret_cast<const uint8 *>(fdt_paddr) + bswap32(hdr->off_dt_struct);
- const char *dt_strings =
- reinterpret_cast<const char *>(fdt_paddr) + bswap32(hdr->off_dt_strings);
-
- uint32 offset = 0;
- int depth = -1;
-
- // Per-depth cell sizes (inherit from parent on entry)
- uint32 address_cells[16] = {2};
- uint32 size_cells[16] = {1};
- bool in_memory_node = false;
-
- while (true) {
- uint32 token =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset));
- offset += 4;
-
- if (token == FDT_BEGIN_NODE) {
- const char *name = reinterpret_cast<const char *>(dt_struct + offset);
- uint32 name_len = 0;
- while (name[name_len] != '\0')
- name_len++;
- offset = (offset + name_len + 1 + 3) & ~3u; // align to 4 bytes
-
- depth++;
-
- // Inherit parent's cell sizes
- if (depth > 0 && depth < 16) {
- address_cells[depth] = address_cells[depth - 1];
- size_cells[depth] = size_cells[depth - 1];
- }
-
- // Match top-level "memory" or "memory@..." nodes
- in_memory_node = (depth == 1 && str_starts_with(name, "memory"));
-
- } else if (token == FDT_END_NODE) {
- if (depth >= 0)
- depth--;
- in_memory_node = false;
-
- } else if (token == FDT_PROP) {
- uint32 len =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset));
- uint32 nameoff =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset + 4));
- offset += 8;
-
- const char *prop_name = dt_strings + nameoff;
- const uint8 *prop_val = dt_struct + offset;
-
- if (depth >= 0 && depth < 16) {
- // Track cell size overrides at the current depth
- if (str_equals(prop_name, "#address-cells")) {
- address_cells[depth] =
- bswap32(*reinterpret_cast<const uint32 *>(prop_val));
- } else if (str_equals(prop_name, "#size-cells")) {
- size_cells[depth] =
- bswap32(*reinterpret_cast<const uint32 *>(prop_val));
- }
- // Decode the "reg" property using the parent's cell sizes
- else if (in_memory_node && str_equals(prop_name, "reg")) {
- uint32 p_depth = (depth > 0) ? depth - 1 : 0;
- *base = read_cells(prop_val, address_cells[p_depth]);
- *size = read_cells(prop_val + address_cells[p_depth] * 4,
- size_cells[p_depth]);
- return; // Success
- }
- }
-
- offset = (offset + len + 3) & ~3u; // align to 4 bytes
-
- } else if (token == FDT_NOP) {
- continue;
- } else if (token == FDT_END) {
- break;
- } else {
- kernel_panic("FDT early_scan_mem: unknown token", nullptr);
- }
- }
-
- kernel_panic("FDT early_scan_mem: /memory node not found", nullptr);
-}
-
-/* ------------------------------------------------------------------ */
-/* Phase 1: Early scan — UART address */
-/* ------------------------------------------------------------------ */
-
-/*
- * Locate a UART node by matching compatible = "ns16550a" and return
- * its base address from the "reg" property.
- *
- * Unlike early_scan_mem, failure here is non-fatal: we fall back to
- * the platform's default UART address (0x10000000 for QEMU virt).
- */
-void FdtManager::early_scan_uart(uint64 fdt_paddr, uint64 *uart_addr,
- uint64 *uart_size) {
- if (!uart_addr || !uart_size)
- return;
-
- auto set_default_uart = [uart_addr, uart_size]() {
- *uart_addr = 0x10000000ULL;
- *uart_size = 0x100;
- };
-
- const fdt_header *hdr = reinterpret_cast<const fdt_header *>(fdt_paddr);
- if (bswap32(hdr->magic) != FDT_MAGIC) {
- set_default_uart();
- return;
- }
-
- const uint8 *dt_struct =
- reinterpret_cast<const uint8 *>(fdt_paddr) + bswap32(hdr->off_dt_struct);
- const char *dt_strings =
- reinterpret_cast<const char *>(fdt_paddr) + bswap32(hdr->off_dt_strings);
-
- uint32 offset = 0;
- int depth = -1;
- uint32 address_cells[16] = {2};
- uint32 size_cells[16] = {1};
- bool in_uart_node = false;
- bool has_current_node_reg = false;
- uint64 current_node_reg = 0;
- uint64 current_node_size = 0;
-
- while (true) {
- uint32 token =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset));
- offset += 4;
-
- if (token == FDT_BEGIN_NODE) {
- const char *name = reinterpret_cast<const char *>(dt_struct + offset);
- uint32 name_len = 0;
- while (name[name_len] != '\0')
- name_len++;
- offset = (offset + name_len + 1 + 3) & ~3u;
-
- depth++;
- if (depth > 0 && depth < 16) {
- address_cells[depth] = address_cells[depth - 1];
- size_cells[depth] = size_cells[depth - 1];
- }
- in_uart_node = false;
- has_current_node_reg = false;
-
- } else if (token == FDT_END_NODE) {
- if (depth >= 0)
- depth--;
- in_uart_node = false;
- has_current_node_reg = false;
-
- } else if (token == FDT_PROP) {
- uint32 len =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset));
- uint32 nameoff =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset + 4));
- offset += 8;
-
- const char *prop_name = dt_strings + nameoff;
- const uint8 *prop_val = dt_struct + offset;
-
- if (depth >= 0 && depth < 16) {
- if (str_equals(prop_name, "#address-cells")) {
- address_cells[depth] =
- bswap32(*reinterpret_cast<const uint32 *>(prop_val));
- } else if (str_equals(prop_name, "#size-cells")) {
- size_cells[depth] =
- bswap32(*reinterpret_cast<const uint32 *>(prop_val));
- } else if (str_equals(prop_name, "compatible")) {
- if (prop_contains_string(reinterpret_cast<const char *>(prop_val),
- len, "ns16550a")) {
- in_uart_node = true;
- if (has_current_node_reg) {
- *uart_addr = current_node_reg;
- *uart_size = current_node_size;
- return;
- }
- }
- } else if (str_equals(prop_name, "reg")) {
- uint32 p_depth = (depth > 0) ? depth - 1 : 0;
- current_node_reg = read_cells(prop_val, address_cells[p_depth]);
- current_node_size = read_cells(prop_val + address_cells[p_depth] * 4,
- size_cells[p_depth]);
- has_current_node_reg = true;
- if (in_uart_node) {
- *uart_addr = current_node_reg;
- *uart_size = current_node_size;
- return; // Found it
- }
- }
- }
-
- offset = (offset + len + 3) & ~3u;
-
- } else if (token == FDT_NOP) {
- continue;
- } else if (token == FDT_END) {
- break;
- } else {
- break; // Unknown token — give up silently
- }
- }
-
- set_default_uart();
-}
-
-/* ------------------------------------------------------------------ */
-/* Phase 2: Unflatten (stub — requires Slab) */
-/* ------------------------------------------------------------------ */
-
-void FdtManager::unflatten() {
- const uint64 fdt_va = pa_to_va(phys_addr(fdt_paddr_)).raw;
- const fdt_header *hdr = reinterpret_cast<const fdt_header *>(fdt_va);
- if (bswap32(hdr->magic) != FDT_MAGIC)
- kernel_panic("fdt unflatten: invalid magic", nullptr);
-
- const uint8 *dt_struct =
- reinterpret_cast<const uint8 *>(fdt_va) + bswap32(hdr->off_dt_struct);
- const char *dt_strings =
- reinterpret_cast<const char *>(fdt_va) + bswap32(hdr->off_dt_strings);
-
- uint32 offset = 0;
-
- /* Stack to track current node at each depth (max 16 levels) */
- DeviceNode *stack[16] = {};
- int depth = -1;
-
- while (true) {
- uint32 token =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset));
- offset += 4;
-
- if (token == FDT_BEGIN_NODE) {
- const char *name = reinterpret_cast<const char *>(dt_struct + offset);
- uint32 name_len = 0;
- while (name[name_len] != '\0')
- name_len++;
- offset = (offset + name_len + 1 + 3) & ~3u;
-
- depth++;
- if (depth >= 16)
- kernel_panic("fdt unflatten: tree too deep", nullptr);
-
- DeviceNode *parent = (depth > 0) ? stack[depth - 1] : nullptr;
- DeviceNode *node = new DeviceNode(parent, name);
- if (!node)
- kernel_panic("fdt unflatten: OOM creating DeviceNode", nullptr);
-
- stack[depth] = node;
-
- if (parent)
- parent->add_child(node);
- else
- root_ = node; /* Root node */
-
- } else if (token == FDT_END_NODE) {
- if (depth >= 0)
- depth--;
-
- } else if (token == FDT_PROP) {
- uint32 len =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset));
- uint32 nameoff =
- bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset + 4));
- offset += 8;
-
- const char *prop_name = dt_strings + nameoff;
- const void *prop_val = dt_struct + offset;
-
- if (depth >= 0 && depth < 16 && stack[depth]) {
- FdtProperty *prop = new FdtProperty;
- if (!prop)
- kernel_panic("fdt unflatten: OOM creating FdtProperty", nullptr);
- prop->name = prop_name;
- prop->value = prop_val;
- prop->len = static_cast<int>(len);
- prop->link.init();
- stack[depth]->add_prop(prop);
- }
-
- offset = (offset + len + 3) & ~3u;
-
- } else if (token == FDT_NOP) {
- continue;
- } else if (token == FDT_END) {
- break;
- } else {
- kernel_panic("fdt unflatten: unknown token", nullptr);
- }
- }
-
- is_unflattened_ = true;
-}
-
-/* Recursive helper: find first node with matching compatible string */
-static DeviceNode *find_compat_recursive(DeviceNode *node, const char *compat) {
- const FdtProperty *prop = node->get_prop("compatible");
- if (prop) {
- const char *val = static_cast<const char *>(prop->value);
- uint32 i = 0;
- while (i < static_cast<uint32>(prop->len)) {
- const char *s = val + i;
- if (str_equals(s, compat))
- return node;
- while (i < static_cast<uint32>(prop->len) && val[i] != '\0')
- i++;
- i++;
- }
- }
-
- /* Search children */
- list_node *cur = node->children_list_head();
- list_node *end = node->children_list_sentinel();
- while (cur != end) {
- DeviceNode *child = list_entry<DeviceNode, &DeviceNode::sibling_link_>(cur);
- DeviceNode *found = find_compat_recursive(child, compat);
- if (found)
- return found;
- cur = cur->next;
- }
- return nullptr;
-}
-
-DeviceNode *FdtManager::find_compatible(const char *compat_string) {
- if (!is_unflattened_)
- kernel_panic("fdt: tree not unflattened yet", nullptr);
- if (!root_)
- return nullptr;
- return find_compat_recursive(root_, compat_string);
-}
-
-DeviceNode *FdtManager::get_node_by_path(const char *path) {
- if (!is_unflattened_)
- kernel_panic("fdt: tree not unflattened yet", nullptr);
- if (!root_ || !path)
- return nullptr;
-
- /* Skip leading '/' */
- if (*path == '/')
- path++;
- if (*path == '\0')
- return root_;
-
- DeviceNode *cur = root_;
- while (*path && cur) {
- /* Extract next path component */
- const char *end = path;
- while (*end && *end != '/')
- end++;
-
- /* Search children for a name matching this component */
- bool found = false;
- list_node *lc = cur->children_list_head();
- list_node *le = cur->children_list_sentinel();
- while (lc != le) {
- DeviceNode *child =
- list_entry<DeviceNode, &DeviceNode::sibling_link_>(lc);
- /* Compare name up to the component length */
- const char *cn = child->get_name();
- const char *p = path;
- const char *e = end;
- bool match = true;
- while (p < e) {
- if (*cn != *p) {
- match = false;
- break;
- }
- cn++;
- p++;
- }
- if (match && *cn == '\0') {
- cur = child;
- found = true;
- break;
- }
- lc = lc->next;
- }
-
- if (!found)
- return nullptr;
-
- path = (*end == '/') ? end + 1 : end;
- }
- return cur;
-}
-
-/* ------------------------------------------------------------------ */
-/* Public free-function API */
-/* ------------------------------------------------------------------ */
-
-void fdt_init(uint64 fdt_paddr) {
- const uint32 *magic_ptr = reinterpret_cast<const uint32 *>(fdt_paddr);
- if (bswap32(*magic_ptr) != FDT_MAGIC)
- kernel_panic("fdt_init: invalid FDT magic number", nullptr);
-
- g_fdt = new (g_fdt_buf) FdtManager(fdt_paddr);
-}
-
-void fdt_unflatten() { g_fdt->unflatten(); }
-
-DeviceNode *fdt_find_compatible(const char *compat_string) {
- return g_fdt->find_compatible(compat_string);
-}
-
-DeviceNode *fdt_get_node_by_path(const char *path) {
- return g_fdt->get_node_by_path(path);
-}
-
-void fdt_early_get_mem_info(uint64 *base, uint64 *size) {
- FdtManager::early_scan_mem(g_fdt->get_paddr(), base, size);
-}
-
-void fdt_early_get_uart_info(uint64 *uart_addr, uint64 *uart_size) {
- FdtManager::early_scan_uart(g_fdt->get_paddr(), uart_addr, uart_size);
-}
-
-void FdtManager::early_scan_kaslr(uint64 fdt_paddr, uint64 *seed)
-{
- if (!seed) return;
- *seed = 0;
-
- const fdt_header *hdr = reinterpret_cast<const fdt_header *>(fdt_paddr);
- if (bswap32(hdr->magic) != FDT_MAGIC) return;
-
- const uint8 *dt_struct = reinterpret_cast<const uint8 *>(fdt_paddr) + bswap32(hdr->off_dt_struct);
- const char *dt_strings = reinterpret_cast<const char *>(fdt_paddr) + bswap32(hdr->off_dt_strings);
-
- uint32 offset = 0;
- int depth = -1;
- bool in_chosen = false;
-
- while (true) {
- uint32 token = bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset));
- offset += 4;
-
- if (token == FDT_BEGIN_NODE) {
- const char *name = reinterpret_cast<const char *>(dt_struct + offset);
- uint32 name_len = 0;
- while (name[name_len] != '\0') name_len++;
- offset = (offset + name_len + 1 + 3) & ~3u;
-
- depth++;
- if (depth == 1 && str_starts_with(name, "chosen")) {
- in_chosen = true;
- } else if (depth == 1) {
- in_chosen = false;
- }
- } else if (token == FDT_END_NODE) {
- if (depth >= 0) depth--;
- if (depth < 1) in_chosen = false;
- } else if (token == FDT_PROP) {
- uint32 len = bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset));
- uint32 nameoff = bswap32(*reinterpret_cast<const uint32 *>(dt_struct + offset + 4));
- offset += 8;
-
- if (in_chosen) {
- const char *prop_name = dt_strings + nameoff;
- if (str_starts_with(prop_name, "kaslr-seed") && len == 8) {
- *seed = read_cells(dt_struct + offset, 2);
- return;
- }
- }
- offset = (offset + len + 3) & ~3u;
- } else if (token == FDT_NOP) {
- continue;
- } else if (token == FDT_END) {
- break;
- } else {
- break;
- }
- }
-}
-
-void fdt_early_get_kaslr_seed(uint64 *seed) {
- FdtManager::early_scan_kaslr(g_fdt->get_paddr(), seed);
-}
diff --git a/na/fdt/module.mk b/na/fdt/module.mk
deleted file mode 100644
index 3e490ca..0000000
--- a/na/fdt/module.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-# fdt/module.mk
-SRCS_CC += fdt/fdt.cc
-SRCS_CC += fdt/device_node.cc
diff --git a/na/fs/chardev.cc b/na/fs/chardev.cc
deleted file mode 100644
index fa96276..0000000
--- a/na/fs/chardev.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * chardev.cc — Character device registry
- *
- * Simple linked list of registered cdevs, searched by dev number.
- */
-
-#include <lume/chardev.h>
-#include <lume/list.h>
-#include <lume/klog.h>
-#include <kernel/sync/spinlock.h>
-
-static list_node g_cdev_list;
-static Spinlock g_cdev_lock;
-
-void cdev_init()
-{
- list_init(&g_cdev_list);
-}
-
-void cdev_register(cdev* dev)
-{
- LockGuard guard(g_cdev_lock);
- list_init(&dev->list);
- list_add_tail(&dev->list, &g_cdev_list);
- printk("[cdev] registered '%s' (%d,%d)\n",
- dev->name, MAJOR(dev->dev), MINOR(dev->dev));
-}
-
-cdev* cdev_find(uint32 dev_nr)
-{
- LockGuard guard(g_cdev_lock);
- list_node* pos;
- for (pos = g_cdev_list.prev; pos != &g_cdev_list; pos = pos->prev) {
- cdev* cd = container_of(pos, cdev, list);
- if (cd->dev == dev_nr) {
- return cd;
- }
- }
- return nullptr;
-}
diff --git a/na/fs/dcache.cc b/na/fs/dcache.cc
deleted file mode 100644
index 94ab287..0000000
--- a/na/fs/dcache.cc
+++ /dev/null
@@ -1,287 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * dcache.cc — Directory entry cache
- *
- * Provides dentry allocation, lookup, instantiation, and d_path.
- */
-
-#include <lume/dcache.h>
-#include <lume/fs.h>
-#include <lume/file.h>
-#include <lume/slab.h>
-#include <lume/errno.h>
-#include <lume/fs_types.h>
-
-/* ================================================================== */
-/* Hash function */
-/* ================================================================== */
-
-uint32 full_name_hash(const char* name, uint32 len)
-{
- uint32 hash = 0;
- for (uint32 i = 0; i < len; i++)
- hash = hash * 31 + static_cast<uint8>(name[i]);
- return hash;
-}
-
-/* ================================================================== */
-/* d_set_type — set DCACHE_*_TYPE bits from inode mode */
-/* ================================================================== */
-
-void d_set_type(dentry* de, uint32 i_mode)
-{
- de->d_flags &= ~DCACHE_ENTRY_TYPE;
-
- if (S_ISDIR(i_mode))
- de->d_flags |= DCACHE_DIRECTORY_TYPE;
- else if (S_ISLNK(i_mode))
- de->d_flags |= DCACHE_SYMLINK_TYPE;
- else if (S_ISREG(i_mode))
- de->d_flags |= DCACHE_REGULAR_TYPE;
- else
- de->d_flags |= DCACHE_SPECIAL_TYPE;
-}
-
-/* ================================================================== */
-/* d_alloc — allocate a dentry under a parent */
-/* ================================================================== */
-
-static void dentry_init(dentry* de, dentry* parent, const qstr* name)
-{
- de->d_flags = DCACHE_MISS_TYPE; /* negative until instantiated */
- de->d_parent = parent ? parent : de;
- de->d_inode = nullptr;
- de->d_op = nullptr;
- de->d_sb = parent ? parent->d_sb : nullptr;
- de->d_time = 0;
- de->d_fsdata = nullptr;
- de->d_lockref.store(1);
-
- list_init(&de->d_lru);
- list_init(&de->d_child);
- list_init(&de->d_children);
- list_init(&de->d_alias);
-
- /* Copy name */
- if (name && name->name && name->len > 0) {
- uint32 len = name->len;
- if (len < DNAME_INLINE_LEN) {
- for (uint32 i = 0; i < len; i++)
- de->d_iname[i] = name->name[i];
- de->d_iname[len] = '\0';
- de->d_name.name = reinterpret_cast<const char*>(de->d_iname);
- } else {
- /* Heap-allocate for long names */
- char* buf = new char[len + 1];
- for (uint32 i = 0; i < len; i++)
- buf[i] = name->name[i];
- buf[len] = '\0';
- de->d_name.name = buf;
- }
- de->d_name.len = len;
- de->d_name.hash = name->hash ? name->hash : full_name_hash(name->name, len);
- } else {
- de->d_iname[0] = '\0';
- de->d_name.name = reinterpret_cast<const char*>(de->d_iname);
- de->d_name.len = 0;
- de->d_name.hash = 0;
- }
-}
-
-dentry* d_alloc(dentry* parent, const qstr* name)
-{
- auto* de = new dentry{};
- if (!de) return nullptr;
-
- dentry_init(de, parent, name);
- de->d_sb = parent->d_sb;
-
- /* Use sb default d_op if set */
- if (parent->d_sb->s_d_op)
- de->d_op = parent->d_sb->s_d_op;
-
- /* Link into parent's children list */
- if (parent) {
- LockGuard guard(parent->d_lock);
- list_add_tail(&de->d_child, &parent->d_children);
- dget(parent); /* child holds ref on parent */
- }
-
- return de;
-}
-
-dentry* d_alloc_anon(super_block* sb)
-{
- auto* de = new dentry{};
- if (!de) return nullptr;
-
- dentry_init(de, nullptr, nullptr);
- de->d_sb = sb;
- de->d_parent = de; /* root: parent is self */
-
- return de;
-}
-
-/* ================================================================== */
-/* d_instantiate — bind an inode to a dentry */
-/* ================================================================== */
-
-void d_instantiate(dentry* de, inode* ip)
-{
- if (!ip) return;
-
- LockGuard guard(de->d_lock);
- de->d_inode = ip;
- d_set_type(de, ip->i_mode);
-
- /* Add to inode's alias list */
- list_add_tail(&de->d_alias, &ip->i_dentry);
-}
-
-/* ================================================================== */
-/* d_make_root — create root dentry for a superblock */
-/* ================================================================== */
-
-dentry* d_make_root(inode* root_inode)
-{
- if (!root_inode) return nullptr;
-
- dentry* de = d_alloc_anon(root_inode->i_sb);
- if (!de) {
- iput(root_inode);
- return nullptr;
- }
-
- d_instantiate(de, root_inode);
- return de;
-}
-
-/* ================================================================== */
-/* d_add — combined d_instantiate + d_rehash */
-/* ================================================================== */
-
-void d_add(dentry* de, inode* ip)
-{
- d_instantiate(de, ip);
- /* d_rehash would add to dcache hash table; simplified for now */
-}
-
-/* ================================================================== */
-/* d_lookup — search children of parent for a name match */
-/* ================================================================== */
-
-dentry* d_lookup(const dentry* parent, const qstr* name)
-{
- LockGuard guard(const_cast<Spinlock&>(parent->d_lock));
-
- list_node* pos;
- list_for_each(pos, &parent->d_children) {
- dentry* child = list_entry(pos, dentry, d_child);
-
- if (child->d_name.len != name->len)
- continue;
-
- /* Compare names */
- const char* a = child->d_name.name;
- const char* b = name->name;
- uint32 len = name->len;
- bool match = true;
- for (uint32 i = 0; i < len; i++) {
- if (a[i] != b[i]) { match = false; break; }
- }
- if (match) {
- dget(child);
- return child;
- }
- }
-
- return nullptr;
-}
-
-/* ================================================================== */
-/* dget / dput — dentry reference counting */
-/* ================================================================== */
-
-dentry* dget(dentry* de)
-{
- if (de)
- de->d_lockref.fetch_add(1);
- return de;
-}
-
-void dput(dentry* de)
-{
- if (!de) return;
-
- int old = de->d_lockref.fetch_sub(1);
- if (old > 1) return;
-
- /* Last reference — could free, but for ramfs we keep dentries alive.
- * A real implementation would check d_op->d_delete and possibly
- * add to LRU for later reclaim. For now, just leak (Phase 1). */
-}
-
-/* ================================================================== */
-/* d_drop / d_delete */
-/* ================================================================== */
-
-void d_drop(dentry* de)
-{
- /* Remove from parent's children list */
- LockGuard guard(de->d_lock);
- if (!list_empty(&de->d_child))
- list_del_init(&de->d_child);
-}
-
-void d_delete(dentry* de)
-{
- d_drop(de);
-}
-
-/* ================================================================== */
-/* d_path — build absolute pathname from a path */
-/* ================================================================== */
-
-char* d_path(const path* p, char* buf, int buflen)
-{
- if (!p || !p->de || buflen <= 0)
- return nullptr;
-
- /* Build path by walking up d_parent chain, writing backwards */
- char* end = buf + buflen - 1;
- *end = '\0';
-
- dentry* de = p->de;
-
- if (d_is_root(de)) {
- if (end <= buf) return nullptr;
- *(--end) = '/';
- return end;
- }
-
- while (!d_is_root(de)) {
- uint32 len = de->d_name.len;
- end -= len;
- if (end <= buf) return nullptr;
-
- const char* name = de->d_name.name;
- for (uint32 i = 0; i < len; i++)
- end[i] = name[i];
-
- *(--end) = '/';
- de = de->d_parent;
- }
-
- return end;
-}
-
-/* ================================================================== */
-/* dcache_init */
-/* ================================================================== */
-
-void dcache_init()
-{
- /* Future: init dcache hash table */
-}
diff --git a/na/fs/devfs.cc b/na/fs/devfs.cc
deleted file mode 100644
index b4b2cf3..0000000
--- a/na/fs/devfs.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * devfs.cc — /dev population and chardev-aware open
- *
- * Creates /dev as a subdirectory under the ramfs root, then
- * populates device nodes. On open(), if the inode is S_IFCHR,
- * we look up the cdev by i_rdev and use its fops.
- */
-
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/file.h>
-#include <lume/chardev.h>
-#include <lume/mount.h>
-#include <lume/klog.h>
-#include <lume/errno.h>
-#include <lume/fs_types.h>
-
-/* ================================================================== */
-/* devfs_create_node — create a char device inode under parent */
-/* ================================================================== */
-
-static int devfs_create_node(dentry* parent, const char* name,
- uint32 major, uint32 minor)
-{
- inode* dir_ip = parent->d_inode;
- if (!dir_ip || !dir_ip->i_op || !dir_ip->i_op->mknod) {
- return -EPERM;
- }
-
- qstr q{};
- q.name = name;
- q.len = 0;
- for (const char* s = name; *s; s++) {
- q.len++;
- }
-
- dentry* de = d_alloc(parent, &q);
- if (!de) {
- return -ENOMEM;
- }
-
- int err = dir_ip->i_op->mknod(dir_ip, de, S_IFCHR | 0666,
- MKDEV(major, minor));
- if (err) {
- return err;
- }
-
- return 0;
-}
-
-/* ================================================================== */
-/* devfs_open — chardev-aware open hook */
-/* ================================================================== */
-
-/*
- * Called from sys_openat when the inode is S_IFCHR.
- * Replaces f_op with the chardev's file_operations.
- */
-int devfs_chrdev_open(inode* ip, file* fp)
-{
- if (!S_ISCHR(ip->i_mode)) {
- return 0;
- }
-
- cdev* cd = cdev_find(ip->i_rdev);
- if (!cd || !cd->fops) {
- return -ENXIO; /* No such device */
- }
-
- fp->f_op = cd->fops;
-
- /* Call the device's own open if it has one */
- if (fp->f_op->open) {
- return fp->f_op->open(ip, fp);
- }
-
- return 0;
-}
-
-/* ================================================================== */
-/* devfs_init — create /dev and populate device nodes */
-/* ================================================================== */
-
-void devfs_init()
-{
- if (!g_root_dentry || !g_root_dentry->d_inode) {
- printk(KERN_ERR "[devfs] no root filesystem!\n");
- return;
- }
-
- /* Create /dev directory — or reuse if initramfs already created it.
- * Linux: devtmpfs similarly handles pre-existing directories. */
- inode* root_ip = g_root_dentry->d_inode;
-
- qstr dev_name{};
- dev_name.name = "dev";
- dev_name.len = 3;
- dev_name.hash = full_name_hash("dev", 3);
-
- dentry* dev_de = d_lookup(g_root_dentry, &dev_name);
- if (dev_de) {
- /* /dev already exists (from initramfs) — reuse it */
- printk("[devfs] reusing existing /dev directory\n");
- } else {
- /* Create /dev from scratch */
- if (!root_ip->i_op || !root_ip->i_op->mkdir) {
- printk(KERN_ERR "[devfs] root has no mkdir!\n");
- return;
- }
-
- dev_de = d_alloc(g_root_dentry, &dev_name);
- if (!dev_de) {
- printk(KERN_ERR "[devfs] failed to alloc /dev dentry\n");
- return;
- }
-
- int err = root_ip->i_op->mkdir(root_ip, dev_de, S_IFDIR | 0755);
- if (err) {
- printk(KERN_ERR "[devfs] mkdir /dev failed: %d\n", err);
- return;
- }
- }
-
- /* Populate: /dev/console (5,1) */
- devfs_create_node(dev_de, "console", 5, 1);
-
- /* Populate: /dev/null (1,3) */
- devfs_create_node(dev_de, "null", 1, 3);
-
- /* Populate: /dev/zero (1,5) */
- devfs_create_node(dev_de, "zero", 1, 5);
-
- printk("[devfs] /dev populated (console, null, zero)\n");
-}
diff --git a/na/fs/fd.cc b/na/fs/fd.cc
deleted file mode 100644
index 5fc8ab0..0000000
--- a/na/fs/fd.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * fd.cc — File descriptor table management
- */
-
-#include <lume/fdtable.h>
-#include <lume/file.h>
-#include <lume/slab.h>
-#include <lume/errno.h>
-
-/* ================================================================== */
-/* Bitmap helpers */
-/* ================================================================== */
-
-static inline bool bit_test(const uint64* bmap, int bit) {
- return (bmap[bit / 64] >> (bit % 64)) & 1;
-}
-static inline void bit_set(uint64* bmap, int bit) {
- bmap[bit / 64] |= (1ULL << (bit % 64));
-}
-static inline void bit_clear(uint64* bmap, int bit) {
- bmap[bit / 64] &= ~(1ULL << (bit % 64));
-}
-
-/* ================================================================== */
-/* alloc_files_struct */
-/* ================================================================== */
-
-files_struct* alloc_files_struct()
-{
- auto* files = new files_struct{};
- if (!files) return nullptr;
-
- files->count.store(1);
- files->next_fd = 0;
- files->max_fds = NR_OPEN_DEFAULT;
-
- for (int i = 0; i < NR_OPEN_DEFAULT; i++)
- files->fd_array[i] = nullptr;
-
- int bmap_words = (NR_OPEN_DEFAULT + 63) / 64;
- for (int i = 0; i < bmap_words; i++) {
- files->close_on_exec_init[i] = 0;
- files->open_fds_init[i] = 0;
- }
-
- return files;
-}
-
-/* ================================================================== */
-/* put_files_struct — drop reference, free on last */
-/* ================================================================== */
-
-void put_files_struct(files_struct* files)
-{
- if (!files) return;
-
- int old = files->count.fetch_sub(1);
- if (old > 1) return;
-
- /* Close all open fds */
- for (int i = 0; i < files->max_fds; i++) {
- if (files->fd_array[i]) {
- fput(files->fd_array[i]);
- files->fd_array[i] = nullptr;
- }
- }
-
- delete files;
-}
-
-/* ================================================================== */
-/* dup_fd — duplicate entire fd table (for fork) */
-/* ================================================================== */
-
-files_struct* dup_fd(files_struct* old_files)
-{
- auto* new_files = alloc_files_struct();
- if (!new_files) return nullptr;
-
- LockGuard guard(old_files->file_lock);
-
- for (int i = 0; i < old_files->max_fds; i++) {
- file* fp = old_files->fd_array[i];
- if (fp) {
- get_file(fp);
- new_files->fd_array[i] = fp;
- }
- }
-
- /* Copy bitmaps */
- int bmap_words = (NR_OPEN_DEFAULT + 63) / 64;
- for (int i = 0; i < bmap_words; i++) {
- new_files->open_fds_init[i] = old_files->open_fds_init[i];
- new_files->close_on_exec_init[i] = old_files->close_on_exec_init[i];
- }
-
- new_files->next_fd = old_files->next_fd;
-
- return new_files;
-}
-
-/* ================================================================== */
-/* alloc_fd — find lowest free fd >= start */
-/* ================================================================== */
-
-int alloc_fd(files_struct* files, unsigned int start)
-{
- LockGuard guard(files->file_lock);
-
- for (int fd = static_cast<int>(start); fd < files->max_fds; fd++) {
- if (!bit_test(files->open_fds_init, fd)) {
- bit_set(files->open_fds_init, fd);
- files->fd_array[fd] = nullptr; /* placeholder, fd_install sets real file */
- if (fd >= files->next_fd)
- files->next_fd = fd + 1;
- return fd;
- }
- }
-
- return -EMFILE;
-}
-
-/* ================================================================== */
-/* fd_install — place a file* at a given fd */
-/* ================================================================== */
-
-void fd_install(files_struct* files, int fd, file* fp)
-{
- LockGuard guard(files->file_lock);
- files->fd_array[fd] = fp;
-}
-
-/* ================================================================== */
-/* fcheck — look up file by fd (no refcount bump) */
-/* ================================================================== */
-
-file* fcheck(files_struct* files, int fd)
-{
- if (fd < 0 || fd >= files->max_fds) return nullptr;
- return files->fd_array[fd];
-}
-
-/* ================================================================== */
-/* close_fd */
-/* ================================================================== */
-
-int close_fd(files_struct* files, int fd)
-{
- file* fp;
- {
- LockGuard guard(files->file_lock);
- if (fd < 0 || fd >= files->max_fds) return -EBADF;
-
- fp = files->fd_array[fd];
- if (!fp) return -EBADF;
-
- files->fd_array[fd] = nullptr;
- bit_clear(files->open_fds_init, fd);
- bit_clear(files->close_on_exec_init, fd);
- }
-
- fput(fp);
- return 0;
-}
-
-/* ================================================================== */
-/* do_dup2 — duplicate oldfd to newfd */
-/* ================================================================== */
-
-int do_dup2(files_struct* files, int oldfd, int newfd, int flags)
-{
- if (oldfd == newfd) return -EINVAL;
-
- file* existing = nullptr;
- {
- LockGuard guard(files->file_lock);
-
- if (oldfd < 0 || oldfd >= files->max_fds) return -EBADF;
- if (newfd < 0 || newfd >= files->max_fds) return -EBADF;
-
- file* old_fp = files->fd_array[oldfd];
- if (!old_fp) return -EBADF;
-
- existing = files->fd_array[newfd];
-
- get_file(old_fp);
- files->fd_array[newfd] = old_fp;
- bit_set(files->open_fds_init, newfd);
-
- if (flags & O_CLOEXEC)
- bit_set(files->close_on_exec_init, newfd);
- else
- bit_clear(files->close_on_exec_init, newfd);
- }
-
- if (existing)
- fput(existing);
-
- return newfd;
-}
-
-/* ================================================================== */
-/* do_close_on_exec — close all fds with CLOEXEC set */
-/* ================================================================== */
-
-void do_close_on_exec(files_struct* files)
-{
- for (int fd = 0; fd < files->max_fds; fd++) {
- if (bit_test(files->close_on_exec_init, fd)) {
- close_fd(files, fd);
- }
- }
-}
diff --git a/na/fs/file_table.cc b/na/fs/file_table.cc
deleted file mode 100644
index 06095c1..0000000
--- a/na/fs/file_table.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * file_table.cc — Open file allocation and reference counting
- */
-
-#include <lume/file.h>
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/slab.h>
-#include <lume/errno.h>
-
-/* ================================================================== */
-/* alloc_empty_file */
-/* ================================================================== */
-
-file* alloc_empty_file(int flags)
-{
- auto* fp = new file{};
- if (!fp) return nullptr;
-
- fp->f_count.store(1);
- fp->f_flags = flags;
- fp->f_mode = 0;
- fp->f_op = nullptr;
- fp->f_mapping = nullptr;
- fp->f_inode = nullptr;
- fp->f_pos = 0;
- fp->private_data = nullptr;
- fp->f_path.mnt = nullptr;
- fp->f_path.de = nullptr;
-
- /* Derive f_mode from O_* flags */
- int acc = flags & O_ACCMODE;
- if (acc == O_RDONLY)
- fp->f_mode = FMODE_READ | FMODE_CAN_READ;
- else if (acc == O_WRONLY)
- fp->f_mode = FMODE_WRITE | FMODE_CAN_WRITE;
- else if (acc == O_RDWR)
- fp->f_mode = FMODE_READ | FMODE_WRITE | FMODE_CAN_READ | FMODE_CAN_WRITE;
-
- fp->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
-
- return fp;
-}
-
-/* ================================================================== */
-/* get_file / fput */
-/* ================================================================== */
-
-file* get_file(file* f)
-{
- f->f_count.fetch_add(1);
- return f;
-}
-
-void fput(file* fp)
-{
- if (!fp) return;
-
- int old = fp->f_count.fetch_sub(1);
- if (old > 1) return;
-
- /* Last reference — release */
- if (fp->f_op && fp->f_op->release)
- fp->f_op->release(fp->f_inode, fp);
-
- if (fp->f_path.de)
- dput(fp->f_path.de);
-
- /* Future: mntput(fp->f_path.mnt) */
-
- delete fp;
-}
-
-/* ================================================================== */
-/* vfs_read / vfs_write / vfs_llseek / vfs_fstat */
-/* ================================================================== */
-
-int64 vfs_read(file* fp, char* buf, uint64 count, int64* pos)
-{
- if (!(fp->f_mode & FMODE_CAN_READ))
- return -EBADF;
- if (!fp->f_op || !fp->f_op->read)
- return -EINVAL;
-
- return fp->f_op->read(fp, buf, count, pos);
-}
-
-int64 vfs_write(file* fp, const char* buf, uint64 count, int64* pos)
-{
- if (!(fp->f_mode & FMODE_CAN_WRITE))
- return -EBADF;
- if (!fp->f_op || !fp->f_op->write)
- return -EINVAL;
-
- return fp->f_op->write(fp, buf, count, pos);
-}
-
-int64 vfs_llseek(file* fp, int64 offset, int whence)
-{
- if (fp->f_op && fp->f_op->llseek)
- return fp->f_op->llseek(fp, offset, whence);
- return generic_file_llseek(fp, offset, whence);
-}
-
-int64 generic_file_llseek(file* fp, int64 offset, int whence)
-{
- int64 new_pos;
- switch (whence) {
- case SEEK_SET:
- new_pos = offset;
- break;
- case SEEK_CUR:
- new_pos = fp->f_pos + offset;
- break;
- case SEEK_END:
- new_pos = fp->f_inode->i_size + offset;
- break;
- default:
- return -EINVAL;
- }
-
- if (new_pos < 0) return -EINVAL;
- fp->f_pos = new_pos;
- return new_pos;
-}
-
-int vfs_fstat(file* fp, kstat* stat)
-{
- inode* ip = file_inode(fp);
- if (!ip) return -EBADF;
-
- if (ip->i_op && ip->i_op->getattr) {
- return ip->i_op->getattr(&fp->f_path, stat, 0, 0);
- }
-
- generic_fillattr(ip, stat);
- return 0;
-}
-
-/* ================================================================== */
-/* path_get / path_put (simplified) */
-/* ================================================================== */
-
-void path_get(const path* p)
-{
- if (p->de) dget(p->de);
- /* Future: mntget(p->mnt) */
-}
-
-void path_put(const path* p)
-{
- if (p->de) dput(p->de);
- /* Future: mntput(p->mnt) */
-}
-
-/* ================================================================== */
-/* mntget / mntput (stubs for Phase 1) */
-/* ================================================================== */
-
-vfsmount* mntget(vfsmount* mnt) { return mnt; }
-void mntput(vfsmount*) {}
diff --git a/na/fs/fs_struct.cc b/na/fs/fs_struct.cc
deleted file mode 100644
index 090819f..0000000
--- a/na/fs/fs_struct.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * fs_struct.cc — Per-task filesystem context (cwd, root)
- */
-
-#include <lume/fs_struct.h>
-#include <lume/slab.h>
-
-/* ================================================================== */
-/* alloc_fs_struct */
-/* ================================================================== */
-
-fs_struct* alloc_fs_struct()
-{
- auto* fs = new fs_struct{};
- if (!fs) return nullptr;
-
- fs->count.store(1);
- fs->umask = 022;
- fs->root = {nullptr, nullptr};
- fs->pwd = {nullptr, nullptr};
-
- return fs;
-}
-
-/* ================================================================== */
-/* put_fs_struct */
-/* ================================================================== */
-
-void put_fs_struct(fs_struct* fs)
-{
- if (!fs) return;
-
- int old = fs->count.fetch_sub(1);
- if (old > 1) return;
-
- path_put(&fs->root);
- path_put(&fs->pwd);
- delete fs;
-}
-
-/* ================================================================== */
-/* copy_fs_struct — for fork without CLONE_FS */
-/* ================================================================== */
-
-fs_struct* copy_fs_struct(const fs_struct* old_fs)
-{
- auto* fs = alloc_fs_struct();
- if (!fs) return nullptr;
-
- fs->umask = old_fs->umask;
-
- fs->root = old_fs->root;
- path_get(&fs->root);
-
- fs->pwd = old_fs->pwd;
- path_get(&fs->pwd);
-
- return fs;
-}
-
-/* ================================================================== */
-/* set_fs_root / set_fs_pwd */
-/* ================================================================== */
-
-void set_fs_root(fs_struct* fs, const path* p)
-{
- path old_root;
- {
- LockGuard guard(fs->lock);
- old_root = fs->root;
- fs->root = *p;
- path_get(p);
- }
- path_put(&old_root);
-}
-
-void set_fs_pwd(fs_struct* fs, const path* p)
-{
- path old_pwd;
- {
- LockGuard guard(fs->lock);
- old_pwd = fs->pwd;
- fs->pwd = *p;
- path_get(p);
- }
- path_put(&old_pwd);
-}
diff --git a/na/fs/initramfs.cc b/na/fs/initramfs.cc
deleted file mode 100644
index ba10a3e..0000000
--- a/na/fs/initramfs.cc
+++ /dev/null
@@ -1,285 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * initramfs.cc — Unpack embedded CPIO archive into ramfs
- *
- * The build system embeds a newc-format CPIO archive into the kernel
- * ELF as the section .init.ramfs. The linker script exports
- * __initramfs_start / __initramfs_end symbols.
- *
- * At boot, populate_rootfs() walks the CPIO entries and creates
- * directories and files in the already-mounted ramfs root.
- *
- * Reference: Linux init/initramfs.c
- *
- * CPIO "newc" header format (110 bytes, ASCII hex fields):
- * magic[6] "070701"
- * ino[8] mode[8] uid[8] gid[8] nlink[8] mtime[8]
- * filesize[8] devmajor[8] devminor[8] rdevmajor[8] rdevminor[8]
- * namesize[8] check[8]
- * filename (namesize bytes, NUL terminated, padded to 4-byte boundary)
- * file data (filesize bytes, padded to 4-byte boundary)
- */
-
-#include <lume/klog.h>
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/file.h>
-#include <lume/errno.h>
-#include <lume/fs_types.h>
-
-/* Linker-provided symbols for the embedded CPIO archive */
-extern "C" const char __initramfs_start[];
-extern "C" const char __initramfs_end[];
-
-/* ramfs inode creation (from fs/ramfs/inode.cc) */
-extern inode* ramfs_get_inode(super_block* sb, const inode* dir,
- uint32 mode, uint32 rdev);
-
-/* Global root dentry (from fs/vfs_init.cc) */
-extern dentry* g_root_dentry;
-
-/* ================================================================== */
-/* CPIO newc header parsing */
-/* ================================================================== */
-
-struct cpio_header {
- char magic[6];
- char ino[8];
- char mode[8];
- char uid[8];
- char gid[8];
- char nlink[8];
- char mtime[8];
- char filesize[8];
- char devmajor[8];
- char devminor[8];
- char rdevmajor[8];
- char rdevminor[8];
- char namesize[8];
- char check[8];
-};
-
-static constexpr int kCpioHeaderSize = 110;
-
-static uint32 parse_hex8(const char* s)
-{
- uint32 val = 0;
- for (int i = 0; i < 8; i++) {
- char c = s[i];
- uint32 nibble;
- if (c >= '0' && c <= '9') nibble = c - '0';
- else if (c >= 'a' && c <= 'f') nibble = c - 'a' + 10;
- else if (c >= 'A' && c <= 'F') nibble = c - 'A' + 10;
- else return val;
- val = (val << 4) | nibble;
- }
- return val;
-}
-
-static inline uint32 align4(uint32 v)
-{
- return (v + 3) & ~3U;
-}
-
-/* ================================================================== */
-/* Path resolution helpers */
-/* ================================================================== */
-
-static dentry* cpio_mkdir_p(dentry* root, const char* path,
- const char** out_name)
-{
- dentry* cur = root;
-
- while (*path == '/') path++;
-
- while (true) {
- const char* slash = path;
- while (*slash && *slash != '/') slash++;
-
- if (*slash == '\0') {
- *out_name = path;
- return cur;
- }
-
- uint32 len = static_cast<uint32>(slash - path);
-
- qstr name;
- name.name = path;
- name.len = len;
- name.hash = full_name_hash(path, len);
-
- dentry* child = d_lookup(cur, &name);
- if (!child) {
- child = d_alloc(cur, &name);
- if (!child) return nullptr;
-
- inode* ip = ramfs_get_inode(cur->d_inode->i_sb, cur->d_inode,
- S_IFDIR | 0755, 0);
- if (!ip) return nullptr;
-
- d_instantiate(child, ip);
- dget(child);
- inc_nlink(cur->d_inode);
- }
-
- cur = child;
- path = slash + 1;
- while (*path == '/') path++;
- }
-}
-
-/* ================================================================== */
-/* populate_rootfs — the main entry point */
-/* ================================================================== */
-
-void populate_rootfs()
-{
- const char* p = __initramfs_start;
- const char* end = __initramfs_end;
- uint64 archive_size = static_cast<uint64>(end - p);
-
- if (archive_size == 0) {
- printk("[initramfs] no embedded archive found\n");
- return;
- }
-
- printk("[initramfs] unpacking %llu bytes...\n", archive_size);
-
- int file_count = 0;
- int dir_count = 0;
-
- while (p + kCpioHeaderSize <= end) {
- auto* hdr = reinterpret_cast<const cpio_header*>(p);
-
- /* Verify magic */
- if (hdr->magic[0] != '0' || hdr->magic[1] != '7' ||
- hdr->magic[2] != '0' || hdr->magic[3] != '7' ||
- hdr->magic[4] != '0' || hdr->magic[5] != '1') {
- printk("[initramfs] bad cpio magic at offset %llu\n",
- static_cast<uint64>(p - __initramfs_start));
- break;
- }
-
- uint32 mode = parse_hex8(hdr->mode);
- uint32 filesize = parse_hex8(hdr->filesize);
- uint32 namesize = parse_hex8(hdr->namesize);
-
- const char* name = p + kCpioHeaderSize;
- uint32 name_padded = align4(kCpioHeaderSize + namesize);
- const char* data = p + name_padded;
- uint32 data_padded = align4(filesize);
-
- const char* next = p + name_padded + data_padded;
-
- /* Check for TRAILER!!! end-of-archive marker */
- if (namesize == 11 && name[0] == 'T' && name[1] == 'R' &&
- name[2] == 'A' && name[3] == 'I' && name[4] == 'L' &&
- name[5] == 'E' && name[6] == 'R' && name[7] == '!' &&
- name[8] == '!' && name[9] == '!') {
- break;
- }
-
- /* Skip "." entry */
- if (namesize == 2 && name[0] == '.' && name[1] == '\0') {
- p = next;
- continue;
- }
-
- /* Resolve parent directory, creating intermediate dirs */
- const char* basename = nullptr;
- dentry* parent = cpio_mkdir_p(g_root_dentry, name, &basename);
- if (!parent || !basename || basename[0] == '\0') {
- p = next;
- continue;
- }
-
- /* Build qstr for the final component */
- uint32 blen = 0;
- while (basename[blen] && basename[blen] != '/') blen++;
-
- qstr bname;
- bname.name = basename;
- bname.len = blen;
- bname.hash = full_name_hash(basename, blen);
-
- if (S_ISDIR(mode)) {
- dentry* existing = d_lookup(parent, &bname);
- if (!existing) {
- dentry* de = d_alloc(parent, &bname);
- if (de) {
- inode* ip = ramfs_get_inode(parent->d_inode->i_sb,
- parent->d_inode,
- S_IFDIR | (mode & 0777), 0);
- if (ip) {
- d_instantiate(de, ip);
- dget(de);
- inc_nlink(parent->d_inode);
- dir_count++;
- }
- }
- }
- } else if (S_ISREG(mode)) {
- dentry* de = d_alloc(parent, &bname);
- if (de) {
- inode* ip = ramfs_get_inode(parent->d_inode->i_sb,
- parent->d_inode,
- S_IFREG | (mode & 0777), 0);
- if (ip) {
- d_instantiate(de, ip);
- dget(de);
-
- /* Write file content via ramfs write path */
- if (filesize > 0 && ip->i_fop && ip->i_fop->write) {
- file tmp_fp = {};
- tmp_fp.f_inode = ip;
- tmp_fp.f_path.de = de;
- tmp_fp.f_pos = 0;
- tmp_fp.f_flags = O_WRONLY;
- tmp_fp.f_count.store(1);
-
- int64 pos = 0;
- ip->i_fop->write(&tmp_fp, data,
- filesize, &pos);
- }
-
- file_count++;
- printk("[initramfs] %s (%u bytes, mode %03o)\n",
- name, filesize, mode & 0777);
- }
- }
- } else if (S_ISLNK(mode)) {
- /* Symlink: file data is the target path (not NUL-terminated).
- * Linux: init/initramfs.c do_symlink() */
- dentry* de = d_alloc(parent, &bname);
- if (de) {
- inode* ip = ramfs_get_inode(parent->d_inode->i_sb,
- parent->d_inode,
- S_IFLNK | 0777, 0);
- if (ip) {
- /* Store symlink target in inode.
- * Allocate a kernel buffer and copy the target. */
- char* target = new char[filesize + 1];
- if (target) {
- for (uint32 i = 0; i < filesize; i++)
- target[i] = data[i];
- target[filesize] = '\0';
- ip->i_link = target;
- }
-
- d_instantiate(de, ip);
- dget(de);
- file_count++;
- printk("[initramfs] %s -> %s (symlink)\n",
- name, target ? target : "?");
- }
- }
- }
-
- p = next;
- }
-
- printk("[initramfs] done: %d files, %d dirs\n",
- file_count, dir_count);
-}
diff --git a/na/fs/inode.cc b/na/fs/inode.cc
deleted file mode 100644
index 1d3d006..0000000
--- a/na/fs/inode.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * inode.cc — Inode allocation, lifecycle, and generic helpers
- */
-
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/slab.h>
-#include <lume/errno.h>
-
-namespace {
-lume::atomic<uint64> g_next_ino{1};
-} // namespace
-
-/* ================================================================== */
-/* new_inode — allocate a fresh inode on a superblock */
-/* ================================================================== */
-
-inode* new_inode(super_block* sb)
-{
- inode* ip = nullptr;
-
- if (sb->s_op && sb->s_op->alloc_inode)
- ip = sb->s_op->alloc_inode(sb);
- else
- ip = new inode{};
-
- if (!ip) return nullptr;
-
- /* Zero-init key fields */
- ip->i_sb = sb;
- ip->i_ino = g_next_ino.fetch_add(1);
- ip->i_mode = 0;
- ip->i_flags = 0;
- ip->i_uid = 0;
- ip->i_gid = 0;
- ip->i_nlink = 1;
- ip->i_rdev = 0;
- ip->i_size = 0;
- ip->i_blocks = 0;
- ip->i_bytes = 0;
- ip->i_blkbits = sb->s_blocksize_bits;
- ip->i_generation = 0;
- ip->i_state = I_NEW;
- ip->i_count.store(1);
- ip->i_writecount.store(0);
-
- ip->i_atime_sec = ip->i_mtime_sec = ip->i_ctime_sec = 0;
- ip->i_atime_nsec = ip->i_mtime_nsec = ip->i_ctime_nsec = 0;
-
- ip->i_op = nullptr;
- ip->i_fop = nullptr;
- ip->i_mapping = &ip->i_data;
- ip->i_private = nullptr;
- ip->i_pipe = nullptr;
-
- /* Init embedded address_space */
- ip->i_data.host = ip;
- ip->i_data.a_ops = nullptr;
- ip->i_data.nrpages = 0;
- ip->i_data.flags = 0;
-
- /* Init lists */
- list_init(&ip->i_sb_list);
- list_init(&ip->i_dentry);
-
- /* Add to superblock's inode list */
- {
- LockGuard guard(sb->s_inode_list_lock);
- list_add_tail(&ip->i_sb_list, &sb->s_inodes);
- }
-
- return ip;
-}
-
-/* ================================================================== */
-/* ihold / iput — reference counting */
-/* ================================================================== */
-
-void ihold(inode* ip)
-{
- ip->i_count.fetch_add(1);
-}
-
-void iput(inode* ip)
-{
- if (!ip) return;
-
- int old = ip->i_count.fetch_sub(1);
- if (old > 1) return;
-
- /* Last reference dropped — evict */
- if (ip->i_sb->s_op && ip->i_sb->s_op->evict_inode)
- ip->i_sb->s_op->evict_inode(ip);
-
- /* Remove from sb list */
- {
- LockGuard guard(ip->i_sb->s_inode_list_lock);
- list_del(&ip->i_sb_list);
- }
-
- if (ip->i_sb->s_op && ip->i_sb->s_op->destroy_inode)
- ip->i_sb->s_op->destroy_inode(ip);
- else
- delete ip;
-}
-
-/* ================================================================== */
-/* nlink helpers */
-/* ================================================================== */
-
-void set_nlink(inode* ip, uint32 nlink) { ip->i_nlink = nlink; }
-void inc_nlink(inode* ip) { ip->i_nlink++; }
-void drop_nlink(inode* ip) { ip->i_nlink--; }
-void clear_nlink(inode* ip) { ip->i_nlink = 0; }
-
-/* ================================================================== */
-/* inode_init_owner */
-/* ================================================================== */
-
-void inode_init_owner(inode* ip, const inode* dir, uint32 mode)
-{
- ip->i_uid = 0; /* Phase 1: always root */
- ip->i_gid = 0;
- ip->i_mode = mode;
-}
-
-/* ================================================================== */
-/* generic_fillattr — fill kstat from inode (used by getattr) */
-/* ================================================================== */
-
-void generic_fillattr(inode* ip, kstat* stat)
-{
- stat->st_dev = ip->i_sb->s_dev;
- stat->st_ino = ip->i_ino;
- stat->st_mode = ip->i_mode;
- stat->st_nlink = ip->i_nlink;
- stat->st_uid = ip->i_uid;
- stat->st_gid = ip->i_gid;
- stat->st_rdev = ip->i_rdev;
- stat->__pad = 0;
- stat->st_size = ip->i_size;
- stat->st_blksize = 1 << ip->i_blkbits;
- stat->__pad2 = 0;
- stat->st_blocks = ip->i_blocks;
- stat->st_atime_sec = ip->i_atime_sec;
- stat->st_atime_nsec = ip->i_atime_nsec;
- stat->st_mtime_sec = ip->i_mtime_sec;
- stat->st_mtime_nsec = ip->i_mtime_nsec;
- stat->st_ctime_sec = ip->i_ctime_sec;
- stat->st_ctime_nsec = ip->i_ctime_nsec;
- stat->__unused[0] = 0;
- stat->__unused[1] = 0;
-}
-
-/* ================================================================== */
-/* mark_inode_dirty (stub) */
-/* ================================================================== */
-
-void mark_inode_dirty(inode* ip)
-{
- ip->i_state |= I_DIRTY_SYNC;
- /* Phase 1: no writeback, ramfs is memory-only */
-}
diff --git a/na/fs/module.mk b/na/fs/module.mk
deleted file mode 100644
index f1be157..0000000
--- a/na/fs/module.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-# fs/module.mk — VFS subsystem build rules
-
-SRCS_CC += fs/super.cc
-SRCS_CC += fs/inode.cc
-SRCS_CC += fs/dcache.cc
-SRCS_CC += fs/file_table.cc
-SRCS_CC += fs/fd.cc
-SRCS_CC += fs/fs_struct.cc
-SRCS_CC += fs/namei.cc
-SRCS_CC += fs/ramfs/inode.cc
-SRCS_CC += fs/ramfs/file.cc
-SRCS_CC += fs/ramfs/super.cc
-SRCS_CC += fs/vfs_init.cc
-SRCS_CC += fs/chardev.cc
-SRCS_CC += fs/devfs.cc
-SRCS_CC += fs/pipe.cc
-SRCS_CC += fs/initramfs.cc
diff --git a/na/fs/namei.cc b/na/fs/namei.cc
deleted file mode 100644
index 9124ea9..0000000
--- a/na/fs/namei.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * namei.cc — Pathname resolution (path lookup)
- *
- * Simplified Phase 1: no symlink, no RCU, no mount traversal.
- */
-
-#include <lume/namei.h>
-#include <lume/dcache.h>
-#include <lume/fs.h>
-#include <lume/file.h>
-#include <lume/fdtable.h>
-#include <lume/fs_struct.h>
-#include <lume/mount.h>
-#include <lume/task.h>
-#include <lume/errno.h>
-#include <lume/fs_types.h>
-
-/* ================================================================== */
-/* Helpers */
-/* ================================================================== */
-
-static const char* next_component(const char* path, qstr* result)
-{
- while (*path == '/') path++;
- if (*path == '\0') return nullptr;
-
- const char* start = path;
- while (*path != '\0' && *path != '/') path++;
-
- uint32 len = static_cast<uint32>(path - start);
- result->name = start;
- result->len = len;
- result->hash = full_name_hash(start, len);
-
- return path;
-}
-
-static bool is_dot(const qstr* name) {
- return name->len == 1 && name->name[0] == '.';
-}
-static bool is_dotdot(const qstr* name) {
- return name->len == 2 && name->name[0] == '.' && name->name[1] == '.';
-}
-
-/* ================================================================== */
-/* walk_component */
-/* ================================================================== */
-
-static dentry* walk_component(dentry* parent, const qstr* name)
-{
- if (!parent->d_inode || !S_ISDIR(parent->d_inode->i_mode))
- return reinterpret_cast<dentry*>(-ENOTDIR);
-
- if (is_dot(name))
- return dget(parent);
-
- if (is_dotdot(name))
- return dget(parent->d_parent);
-
- /* Try dcache */
- dentry* child = d_lookup(parent, name);
- if (child)
- return child;
-
- /* Cache miss — call filesystem lookup */
- inode* dir = parent->d_inode;
- if (!dir->i_op || !dir->i_op->lookup)
- return reinterpret_cast<dentry*>(-ENOENT);
-
- child = d_alloc(parent, name);
- if (!child)
- return reinterpret_cast<dentry*>(-ENOMEM);
-
- dentry* result = dir->i_op->lookup(dir, child, 0);
- if (result) {
- dput(child);
- return result;
- }
-
- /* If lookup didn't set an inode, this is a negative dentry
- * (file doesn't exist). Remove from dcache and return error. */
- if (!child->d_inode) {
- d_drop(child);
- dput(child);
- return reinterpret_cast<dentry*>(-ENOENT);
- }
-
- return child;
-}
-
-/* ================================================================== */
-/* filename_lookup */
-/* ================================================================== */
-
-int filename_lookup(int dfd, const char* name, unsigned int flags, path* result)
-{
- if (!name || !result)
- return -EINVAL;
-
- TaskControlBlock* task = current_task();
- dentry* start;
-
- /* Determine starting dentry */
- if (name[0] == '/') {
- start = task->fs->root.de;
- if (!start) start = g_root_dentry;
- name++;
- } else if (dfd == AT_FDCWD) {
- start = task->fs->pwd.de;
- if (!start) start = g_root_dentry;
- } else {
- file* dir_fp = fcheck(task->files, dfd);
- if (!dir_fp) return -EBADF;
- start = dir_fp->f_path.de;
- if (!start || !start->d_inode || !S_ISDIR(start->d_inode->i_mode))
- return -ENOTDIR;
- }
-
- dentry* current_de = dget(start);
- const char* p = name;
-
- while (p && *p) {
- qstr component;
- const char* next = next_component(p, &component);
- if (!next && component.len == 0)
- break;
-
- const char* after = next;
- while (after && *after == '/') after++;
- bool is_last = (!after || *after == '\0');
-
- if (is_last && (flags & LOOKUP_PARENT)) {
- result->de = current_de;
- result->mnt = task->fs->root.mnt;
- return 0;
- }
-
- dentry* child = walk_component(current_de, &component);
-
- int64 err = reinterpret_cast<int64>(child);
- if (err < 0 && err >= -4095) {
- dput(current_de);
- return static_cast<int>(err);
- }
-
- /* ---- Symlink following (Linux: step_into → pick_link) ----
- * If the resolved component is a symlink, follow it.
- * Linux limits this to MAXSYMLINKS (40); we use 8. */
- constexpr int kMaxSymlinks = 8;
- int symlink_depth = 0;
- while (child->d_inode && S_ISLNK(child->d_inode->i_mode) &&
- child->d_inode->i_link && symlink_depth < kMaxSymlinks) {
- symlink_depth++;
- const char* target = child->d_inode->i_link;
- dput(child);
-
- /* Absolute symlink: restart from root.
- * Relative symlink: resolve from current parent. */
- dentry* link_start;
- if (target[0] == '/') {
- link_start = dget(task->fs->root.de ? task->fs->root.de : g_root_dentry);
- target++;
- } else {
- link_start = dget(current_de);
- }
-
- /* Walk each component of the symlink target */
- dentry* link_cur = link_start;
- const char* lp = target;
- qstr lcomp;
- while (lp && *lp) {
- const char* lnext = next_component(lp, &lcomp);
- if (!lnext && lcomp.len == 0) break;
-
- dentry* lchild = walk_component(link_cur, &lcomp);
- int64 lerr = reinterpret_cast<int64>(lchild);
- if (lerr < 0 && lerr >= -4095) {
- dput(link_cur);
- dput(current_de);
- return static_cast<int>(lerr);
- }
-
- dput(link_cur);
- link_cur = lchild;
- lp = lnext;
- }
-
- child = link_cur;
- }
-
- if (symlink_depth >= kMaxSymlinks) {
- dput(child);
- dput(current_de);
- return -ELOOP;
- }
-
- if (!is_last && child->d_inode && !S_ISDIR(child->d_inode->i_mode)) {
- dput(child);
- dput(current_de);
- return -ENOTDIR;
- }
-
- if (!is_last && !child->d_inode) {
- dput(child);
- dput(current_de);
- return -ENOENT;
- }
-
- dput(current_de);
- current_de = child;
- p = next;
- }
-
- if ((flags & LOOKUP_DIRECTORY) && current_de->d_inode &&
- !S_ISDIR(current_de->d_inode->i_mode)) {
- dput(current_de);
- return -ENOTDIR;
- }
-
- /* Negative dentry: the path was walked, but the final component
- * does not exist (d_inode == NULL). Return -ENOENT. */
- if (!current_de->d_inode) {
- dput(current_de);
- return -ENOENT;
- }
-
- result->de = current_de;
- result->mnt = task->fs->root.mnt;
- return 0;
-}
-
-int kern_path(const char* name, unsigned int flags, path* result)
-{
- return filename_lookup(AT_FDCWD, name, flags, result);
-}
-
-dentry* lookup_one_len(const char* name, dentry* parent, int len)
-{
- qstr q;
- q.name = name;
- q.len = static_cast<uint32>(len);
- q.hash = full_name_hash(name, q.len);
-
- return walk_component(parent, &q);
-}
-
-void namei_init() {}
diff --git a/na/fs/pipe.cc b/na/fs/pipe.cc
deleted file mode 100644
index f7708f5..0000000
--- a/na/fs/pipe.cc
+++ /dev/null
@@ -1,278 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * pipe.cc — Pipe implementation
- *
- * Implements unidirectional byte-stream pipes with blocking I/O.
- * Each pipe has a 4KB circular buffer. Reads block when empty,
- * writes block when full (unless O_NONBLOCK).
- *
- * When all writers close, read returns 0 (EOF).
- * When all readers close, write returns -EPIPE.
- */
-
-#include <lume/pipe.h>
-#include <lume/file.h>
-#include <lume/fdtable.h>
-#include <lume/errno.h>
-#include <lume/slab.h>
-#include <lume/fs_types.h>
-#include <lume/klog.h>
-#include <lume/sched.h>
-
-/* ================================================================== */
-/* pipe_inode_info lifecycle */
-/* ================================================================== */
-
-pipe_inode_info* alloc_pipe_info()
-{
- auto* pi = new pipe_inode_info{};
- if (!pi) return nullptr;
-
- pi->rd_pos = 0;
- pi->wr_pos = 0;
- pi->len = 0;
- pi->rd_wait.init("pipe_rd");
- pi->wr_wait.init("pipe_wr");
- pi->readers.store(0);
- pi->writers.store(0);
- pi->refcount.store(1);
- return pi;
-}
-
-void free_pipe_info(pipe_inode_info* pipe)
-{
- delete pipe;
-}
-
-static void pipe_get(pipe_inode_info* pi)
-{
- pi->refcount.fetch_add(1);
-}
-
-static void pipe_put(pipe_inode_info* pi)
-{
- if (pi->refcount.fetch_sub(1) == 1) {
- free_pipe_info(pi);
- }
-}
-
-/* ================================================================== */
-/* pipe_read */
-/* ================================================================== */
-
-static int64 pipe_read(file* fp, char* buf, uint64 count, int64* /*pos*/)
-{
- pipe_inode_info* pi = static_cast<pipe_inode_info*>(fp->private_data);
- if (!pi) return -EBADF;
-
- while (true) {
- pi->lock.acquire();
-
- if (pi->len > 0) {
- /* Data available — copy out */
- int64 to_read = static_cast<int64>(count);
- if (to_read > pi->len) to_read = pi->len;
-
- for (int64 i = 0; i < to_read; i++) {
- buf[i] = pi->buf[pi->rd_pos];
- pi->rd_pos = (pi->rd_pos + 1) % PIPE_BUF_SIZE;
- }
- pi->len -= static_cast<int>(to_read);
-
- pi->lock.release();
-
- /* Wake any blocked writers */
- pi->wr_wait.wake_all();
- return to_read;
- }
-
- /* Buffer empty */
- if (pi->writers.load() == 0) {
- /* No writers left — EOF */
- pi->lock.release();
- return 0;
- }
-
- if (fp->f_flags & O_NONBLOCK) {
- pi->lock.release();
- return -EAGAIN;
- }
-
- pi->lock.release();
-
- /* Sleep until data or writer closes */
- pi->rd_wait.sleep();
- }
-}
-
-/* ================================================================== */
-/* pipe_write */
-/* ================================================================== */
-
-static int64 pipe_write(file* fp, const char* buf, uint64 count, int64* /*pos*/)
-{
- pipe_inode_info* pi = static_cast<pipe_inode_info*>(fp->private_data);
- if (!pi) return -EBADF;
-
- if (pi->readers.load() == 0) {
- return -EPIPE; /* Broken pipe */
- }
-
- int64 written = 0;
-
- while (static_cast<uint64>(written) < count) {
- pi->lock.acquire();
-
- if (pi->len < PIPE_BUF_SIZE) {
- /* Space available — copy in */
- int avail = PIPE_BUF_SIZE - pi->len;
- int64 remaining = static_cast<int64>(count) - written;
- int to_write = avail < static_cast<int>(remaining)
- ? avail
- : static_cast<int>(remaining);
-
- for (int i = 0; i < to_write; i++) {
- pi->buf[pi->wr_pos] = buf[written + i];
- pi->wr_pos = (pi->wr_pos + 1) % PIPE_BUF_SIZE;
- }
- pi->len += to_write;
- written += to_write;
-
- pi->lock.release();
-
- /* Wake any blocked readers */
- pi->rd_wait.wake_all();
- continue;
- }
-
- /* Buffer full */
- if (pi->readers.load() == 0) {
- pi->lock.release();
- return written > 0 ? written : -EPIPE;
- }
-
- if (fp->f_flags & O_NONBLOCK) {
- pi->lock.release();
- return written > 0 ? written : -EAGAIN;
- }
-
- pi->lock.release();
-
- /* Sleep until space or reader closes */
- pi->wr_wait.sleep();
- }
-
- return written;
-}
-
-/* ================================================================== */
-/* pipe_release — called on last fput of a pipe fd */
-/* ================================================================== */
-
-static int pipe_read_release(inode* /*ip*/, file* fp)
-{
- pipe_inode_info* pi = static_cast<pipe_inode_info*>(fp->private_data);
- if (pi) {
- pi->readers.fetch_sub(1);
- /* Wake writers so they see readers==0 and get EPIPE */
- pi->wr_wait.wake_all();
- pipe_put(pi);
- }
- return 0;
-}
-
-static int pipe_write_release(inode* /*ip*/, file* fp)
-{
- pipe_inode_info* pi = static_cast<pipe_inode_info*>(fp->private_data);
- if (pi) {
- pi->writers.fetch_sub(1);
- /* Wake readers so they see writers==0 and get EOF */
- pi->rd_wait.wake_all();
- pipe_put(pi);
- }
- return 0;
-}
-
-/* ================================================================== */
-/* file_operations tables */
-/* ================================================================== */
-
-static file_operations pipe_read_fops;
-static file_operations pipe_write_fops;
-static bool pipe_fops_initialized = false;
-
-static void pipe_fops_init()
-{
- if (pipe_fops_initialized) return;
-
- pipe_read_fops = {};
- pipe_read_fops.read = pipe_read;
- pipe_read_fops.release = pipe_read_release;
-
- pipe_write_fops = {};
- pipe_write_fops.write = pipe_write;
- pipe_write_fops.release = pipe_write_release;
-
- pipe_fops_initialized = true;
-}
-
-/* ================================================================== */
-/* do_pipe2 — create a pipe, install two fds */
-/* ================================================================== */
-
-int do_pipe2(files_struct* files, int fds[2], int flags)
-{
- pipe_fops_init();
-
- pipe_inode_info* pi = alloc_pipe_info();
- if (!pi) return -ENOMEM;
-
- /* Create read-end file */
- file* rd_fp = alloc_empty_file(O_RDONLY | (flags & O_NONBLOCK));
- if (!rd_fp) {
- free_pipe_info(pi);
- return -ENFILE;
- }
- rd_fp->f_op = &pipe_read_fops;
- rd_fp->private_data = pi;
-
- /* Create write-end file */
- file* wr_fp = alloc_empty_file(O_WRONLY | (flags & O_NONBLOCK));
- if (!wr_fp) {
- fput(rd_fp);
- free_pipe_info(pi);
- return -ENFILE;
- }
- wr_fp->f_op = &pipe_write_fops;
- wr_fp->private_data = pi;
-
- /* Bump pipe refcount for the two files */
- pipe_get(pi); /* wr_fp gets second ref */
- pi->readers.store(1);
- pi->writers.store(1);
-
- /* Allocate fds */
- int rd_fd = alloc_fd(files, 0);
- if (rd_fd < 0) {
- fput(rd_fp);
- fput(wr_fp);
- return rd_fd;
- }
-
- int wr_fd = alloc_fd(files, 0);
- if (wr_fd < 0) {
- close_fd(files, rd_fd);
- fput(wr_fp);
- return wr_fd;
- }
-
- fd_install(files, rd_fd, rd_fp);
- fd_install(files, wr_fd, wr_fp);
-
- fds[0] = rd_fd;
- fds[1] = wr_fd;
-
- return 0;
-}
diff --git a/na/fs/ramfs/file.cc b/na/fs/ramfs/file.cc
deleted file mode 100644
index 6d9a070..0000000
--- a/na/fs/ramfs/file.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * ramfs/file.cc — RamFS file operations (read/write via in-memory pages)
- *
- * Phase 1: simple flat buffer per-inode via i_private.
- * Will migrate to page_cache integration later.
- */
-
-#include <lume/fs.h>
-#include <lume/file.h>
-#include <lume/dcache.h>
-#include <lume/slab.h>
-#include <lume/errno.h>
-#include <lume/fs_types.h>
-
-/*
- * Simplest possible in-memory file storage:
- * i_private points to a dynamically grown buffer.
- */
-struct ramfs_file_data {
- char* buf;
- uint64 capacity;
-};
-
-static ramfs_file_data* get_or_create_data(inode* ip)
-{
- if (!ip->i_private) {
- auto* data = new ramfs_file_data{};
- data->buf = nullptr;
- data->capacity = 0;
- ip->i_private = data;
- }
- return static_cast<ramfs_file_data*>(ip->i_private);
-}
-
-static int ensure_capacity(ramfs_file_data* data, uint64 needed)
-{
- if (needed <= data->capacity) return 0;
-
- /* Round up to page size */
- uint64 new_cap = (needed + 4095) & ~4095ULL;
- char* new_buf = new char[new_cap];
- if (!new_buf) return -ENOMEM;
-
- /* Copy old data */
- if (data->buf && data->capacity > 0) {
- for (uint64 i = 0; i < data->capacity; i++)
- new_buf[i] = data->buf[i];
- }
- /* Zero new region */
- for (uint64 i = data->capacity; i < new_cap; i++)
- new_buf[i] = 0;
-
- delete[] data->buf;
- data->buf = new_buf;
- data->capacity = new_cap;
- return 0;
-}
-
-/* ================================================================== */
-/* ramfs_read */
-/* ================================================================== */
-
-static int64 ramfs_read(file* fp, char* buf, uint64 count, int64* pos)
-{
- inode* ip = file_inode(fp);
- int64 offset = *pos;
-
- if (offset < 0) return -EINVAL;
- if (offset >= ip->i_size) return 0;
-
- int64 avail = ip->i_size - offset;
- if (static_cast<int64>(count) > avail)
- count = static_cast<uint64>(avail);
-
- auto* data = static_cast<ramfs_file_data*>(ip->i_private);
- if (!data || !data->buf) return 0;
-
- /* Copy to user buffer.
- * TODO: use copy_to_user when called from syscall context */
- for (uint64 i = 0; i < count; i++)
- buf[i] = data->buf[offset + i];
-
- *pos += count;
- return static_cast<int64>(count);
-}
-
-/* ================================================================== */
-/* ramfs_write */
-/* ================================================================== */
-
-static int64 ramfs_write(file* fp, const char* buf, uint64 count, int64* pos)
-{
- inode* ip = file_inode(fp);
- int64 offset = *pos;
-
- if (fp->f_flags & O_APPEND)
- offset = ip->i_size;
-
- if (offset < 0) return -EINVAL;
-
- auto* data = get_or_create_data(ip);
- uint64 end = static_cast<uint64>(offset) + count;
-
- int err = ensure_capacity(data, end);
- if (err) return err;
-
- /* Copy from user buffer */
- for (uint64 i = 0; i < count; i++)
- data->buf[offset + i] = buf[i];
-
- if (static_cast<int64>(end) > ip->i_size)
- ip->i_size = end;
-
- *pos = end;
- mark_inode_dirty(ip);
- return static_cast<int64>(count);
-}
-
-/* ================================================================== */
-/* ramfs_dir_iterate — getdents64 */
-/* ================================================================== */
-
-static int ramfs_dir_iterate(file* fp, dir_context* ctx)
-{
- dentry* dir_de = file_dentry(fp);
- inode* dir_ip = file_inode(fp);
- int64 pos = ctx->pos;
- int count = 0;
-
- /* pos 0: emit "." */
- if (pos == 0) {
- if (!ctx->actor(ctx, ".", 1, pos, dir_ip->i_ino, DT_DIR))
- return 0;
- ctx->pos = 1;
- pos = 1;
- count++;
- }
-
- /* pos 1: emit ".." */
- if (pos == 1) {
- uint64 parent_ino = dir_de->d_parent->d_inode
- ? dir_de->d_parent->d_inode->i_ino : dir_ip->i_ino;
- if (!ctx->actor(ctx, "..", 2, pos, parent_ino, DT_DIR))
- return 0;
- ctx->pos = 2;
- pos = 2;
- count++;
- }
-
- /* pos >= 2: iterate children */
- int64 child_idx = 2;
- list_node* node;
- list_for_each(node, &dir_de->d_children) {
- dentry* child = list_entry(node, dentry, d_child);
-
- if (!child->d_inode)
- continue; /* skip negative dentries */
-
- if (child_idx >= pos) {
- uint8 d_type = DT_UNKNOWN;
- if (S_ISDIR(child->d_inode->i_mode)) d_type = DT_DIR;
- else if (S_ISREG(child->d_inode->i_mode)) d_type = DT_REG;
- else if (S_ISCHR(child->d_inode->i_mode)) d_type = DT_CHR;
- else if (S_ISBLK(child->d_inode->i_mode)) d_type = DT_BLK;
- else if (S_ISLNK(child->d_inode->i_mode)) d_type = DT_LNK;
- else if (S_ISFIFO(child->d_inode->i_mode)) d_type = DT_FIFO;
-
- if (!ctx->actor(ctx, child->d_name.name,
- static_cast<int>(child->d_name.len),
- child_idx, child->d_inode->i_ino, d_type))
- return 0;
-
- ctx->pos = child_idx + 1;
- count++;
- }
- child_idx++;
- }
-
- return count;
-}
-
-extern file_operations ramfs_file_operations;
-extern file_operations ramfs_dir_operations;
-
-file_operations ramfs_file_operations;
-file_operations ramfs_dir_operations;
-
-void ramfs_file_ops_init()
-{
- ramfs_file_operations.llseek = nullptr;
- ramfs_file_operations.read = ramfs_read;
- ramfs_file_operations.write = ramfs_write;
- ramfs_file_operations.iterate_shared = nullptr;
- ramfs_file_operations.poll = nullptr;
- ramfs_file_operations.unlocked_ioctl = nullptr;
- ramfs_file_operations.mmap = nullptr;
- ramfs_file_operations.open = nullptr;
- ramfs_file_operations.flush = nullptr;
- ramfs_file_operations.release = nullptr;
- ramfs_file_operations.fsync = nullptr;
-
- ramfs_dir_operations.llseek = nullptr;
- ramfs_dir_operations.read = nullptr;
- ramfs_dir_operations.write = nullptr;
- ramfs_dir_operations.iterate_shared = ramfs_dir_iterate;
- ramfs_dir_operations.poll = nullptr;
- ramfs_dir_operations.unlocked_ioctl = nullptr;
- ramfs_dir_operations.mmap = nullptr;
- ramfs_dir_operations.open = nullptr;
- ramfs_dir_operations.flush = nullptr;
- ramfs_dir_operations.release = nullptr;
- ramfs_dir_operations.fsync = nullptr;
-}
diff --git a/na/fs/ramfs/inode.cc b/na/fs/ramfs/inode.cc
deleted file mode 100644
index c5e49bf..0000000
--- a/na/fs/ramfs/inode.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * ramfs/inode.cc — RamFS inode operations
- *
- * A simple in-memory filesystem (like Linux tmpfs without swap).
- * All data lives in memory via the page cache.
- */
-
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/file.h>
-#include <lume/slab.h>
-#include <lume/errno.h>
-#include <lume/fs_types.h>
-
-/* Forward declarations */
-extern file_operations ramfs_file_operations;
-extern file_operations ramfs_dir_operations;
-extern inode_operations ramfs_dir_inode_operations;
-
-/* ================================================================== */
-/* ramfs_get_inode — allocate + init an inode for ramfs */
-/* ================================================================== */
-
-inode* ramfs_get_inode(super_block* sb, const inode* dir, uint32 mode, uint32 rdev)
-{
- inode* ip = new_inode(sb);
- if (!ip) return nullptr;
-
- inode_init_owner(ip, dir, mode);
- /* TODO: set timestamps to current time when we have a time source */
-
- switch (mode & S_IFMT) {
- case S_IFREG:
- ip->i_op = nullptr; /* regular files don't need inode_ops for Phase 1 */
- ip->i_fop = &ramfs_file_operations;
- break;
- case S_IFDIR:
- ip->i_op = &ramfs_dir_inode_operations;
- ip->i_fop = &ramfs_dir_operations;
- inc_nlink(ip); /* directories start with nlink=2 (. and parent's link) */
- break;
- case S_IFLNK:
- /* Symlink: stub for Phase 1 */
- break;
- default:
- /* Character/block devices: i_rdev */
- ip->i_rdev = rdev;
- break;
- }
-
- return ip;
-}
-
-/* ================================================================== */
-/* Directory inode operations */
-/* ================================================================== */
-
-static dentry* ramfs_lookup(inode* dir, dentry* de, unsigned int flags)
-{
- (void)dir;
- (void)flags;
- /* For ramfs, everything is in dcache. If we get here, it means
- * d_lookup() already failed, so this is a negative dentry.
- * Just return nullptr to let the caller see de->d_inode == NULL. */
- return nullptr;
-}
-
-static int ramfs_create(inode* dir, dentry* de, uint32 mode, bool excl)
-{
- (void)excl;
- inode* ip = ramfs_get_inode(dir->i_sb, dir, mode | S_IFREG, 0);
- if (!ip) return -ENOMEM;
-
- d_instantiate(de, ip);
- dget(de);
- dir->i_size += de->d_name.len;
- mark_inode_dirty(dir);
- return 0;
-}
-
-static int ramfs_mkdir(inode* dir, dentry* de, uint32 mode)
-{
- inode* ip = ramfs_get_inode(dir->i_sb, dir, mode | S_IFDIR, 0);
- if (!ip) return -ENOMEM;
-
- d_instantiate(de, ip);
- dget(de);
- inc_nlink(dir);
- dir->i_size += de->d_name.len;
- mark_inode_dirty(dir);
- return 0;
-}
-
-static int ramfs_rmdir(inode* dir, dentry* de)
-{
- /* Check if directory is empty */
- if (!list_empty(&de->d_children))
- return -ENOTEMPTY;
-
- drop_nlink(de->d_inode); /* remove . link */
- drop_nlink(de->d_inode); /* remove parent's link */
- drop_nlink(dir);
- d_delete(de);
- dir->i_size -= de->d_name.len;
- mark_inode_dirty(dir);
- return 0;
-}
-
-static int ramfs_unlink(inode* dir, dentry* de)
-{
- drop_nlink(de->d_inode);
- d_delete(de);
- dir->i_size -= de->d_name.len;
- mark_inode_dirty(dir);
- return 0;
-}
-
-static int ramfs_link(dentry* old_de, inode* dir, dentry* new_de)
-{
- inode* ip = old_de->d_inode;
- if (!ip) return -ENOENT;
-
- inc_nlink(ip);
- ihold(ip);
- d_instantiate(new_de, ip);
- dget(new_de);
- dir->i_size += new_de->d_name.len;
- mark_inode_dirty(dir);
- return 0;
-}
-
-static int ramfs_mknod(inode* dir, dentry* de, uint32 mode, uint32 rdev)
-{
- inode* ip = ramfs_get_inode(dir->i_sb, dir, mode, rdev);
- if (!ip) return -ENOMEM;
-
- d_instantiate(de, ip);
- dget(de);
- dir->i_size += de->d_name.len;
- mark_inode_dirty(dir);
- return 0;
-}
-
-inode_operations ramfs_dir_inode_operations;
-
-void ramfs_dir_inode_ops_init()
-{
- ramfs_dir_inode_operations.lookup = ramfs_lookup;
- ramfs_dir_inode_operations.create = ramfs_create;
- ramfs_dir_inode_operations.link = ramfs_link;
- ramfs_dir_inode_operations.unlink = ramfs_unlink;
- ramfs_dir_inode_operations.symlink = nullptr;
- ramfs_dir_inode_operations.mkdir = ramfs_mkdir;
- ramfs_dir_inode_operations.rmdir = ramfs_rmdir;
- ramfs_dir_inode_operations.mknod = ramfs_mknod;
- ramfs_dir_inode_operations.rename = nullptr;
- ramfs_dir_inode_operations.setattr = nullptr;
- ramfs_dir_inode_operations.getattr = nullptr;
- ramfs_dir_inode_operations.get_link = nullptr;
- ramfs_dir_inode_operations.permission = nullptr;
-}
diff --git a/na/fs/ramfs/super.cc b/na/fs/ramfs/super.cc
deleted file mode 100644
index 6f929f2..0000000
--- a/na/fs/ramfs/super.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * ramfs/super.cc — RamFS superblock: mount, fill_super, and init
- */
-
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/file.h>
-#include <lume/mount.h>
-#include <lume/slab.h>
-#include <lume/kprintf.h>
-#include <lume/errno.h>
-#include <lume/fs_types.h>
-
-extern inode* ramfs_get_inode(super_block* sb, const inode* dir,
- uint32 mode, uint32 rdev);
-
-/* Superblock allocation helper (defined in super.cc) */
-extern super_block* alloc_super(file_system_type* type);
-
-/* ================================================================== */
-/* ramfs super_operations */
-/* ================================================================== */
-
-static inode* ramfs_alloc_inode(super_block* sb)
-{
- return new inode{};
-}
-
-static void ramfs_destroy_inode(inode* ip)
-{
- /* Free ramfs_file_data if any */
- if (ip->i_private) {
- struct ramfs_file_data {
- char* buf;
- uint64 capacity;
- };
- auto* data = static_cast<ramfs_file_data*>(ip->i_private);
- delete[] data->buf;
- delete data;
- }
- delete ip;
-}
-
-static int ramfs_statfs(dentry* de, kstatfs* buf)
-{
- /* Simple fake values */
- buf->f_type = 0x858458f6; /* RAMFS_MAGIC */
- buf->f_bsize = 4096;
- buf->f_blocks = 0;
- buf->f_bfree = 0;
- buf->f_bavail = 0;
- buf->f_files = 0;
- buf->f_ffree = 0;
- buf->f_namelen = 255;
- buf->f_frsize = 4096;
- buf->f_flags = 0;
- return 0;
-}
-
-static super_operations ramfs_sops;
-
-static void ramfs_sops_init()
-{
- ramfs_sops.alloc_inode = ramfs_alloc_inode;
- ramfs_sops.destroy_inode = ramfs_destroy_inode;
- ramfs_sops.free_inode = nullptr;
- ramfs_sops.dirty_inode = nullptr;
- ramfs_sops.write_inode = nullptr;
- ramfs_sops.drop_inode = nullptr;
- ramfs_sops.evict_inode = nullptr;
- ramfs_sops.put_super = nullptr;
- ramfs_sops.sync_fs = nullptr;
- ramfs_sops.statfs = ramfs_statfs;
- ramfs_sops.umount_begin = nullptr;
-}
-
-/* ================================================================== */
-/* ramfs_fill_super */
-/* ================================================================== */
-
-static int ramfs_fill_super(super_block* sb, void* data, int silent)
-{
- sb->s_op = &ramfs_sops;
- sb->s_magic = 0x858458f6;
- sb->s_blocksize = 4096;
- sb->s_blocksize_bits = 12;
- sb->s_maxbytes = 0x7FFFFFFFFFFFFFFFLL;
- sb->s_time_gran = 1;
-
- /* Create root inode */
- inode* root_ip = ramfs_get_inode(sb, nullptr, S_IFDIR | 0755, 0);
- if (!root_ip) return -ENOMEM;
-
- /* Create root dentry */
- sb->s_root = d_make_root(root_ip);
- if (!sb->s_root)
- return -ENOMEM;
-
- return 0;
-}
-
-/* ================================================================== */
-/* ramfs_mount */
-/* ================================================================== */
-
-static super_block* ramfs_mount(file_system_type* type, int flags,
- const char* dev_name, void* data)
-{
- super_block* sb = alloc_super(type);
- if (!sb) return nullptr;
-
- int err = ramfs_fill_super(sb, data, flags & SB_SILENT);
- if (err) {
- delete sb;
- return nullptr;
- }
-
- return sb;
-}
-
-static void ramfs_kill_sb(super_block* sb)
-{
- /* Phase 1: no cleanup needed for ramfs */
- (void)sb;
-}
-
-/* ================================================================== */
-/* Filesystem type registration */
-/* ================================================================== */
-
-static file_system_type ramfs_fs_type;
-
-extern void ramfs_file_ops_init();
-extern void ramfs_dir_inode_ops_init();
-
-void ramfs_init()
-{
- /* Runtime init to avoid un-relocated absolute pointers in .data */
- ramfs_sops_init();
- ramfs_file_ops_init();
- ramfs_dir_inode_ops_init();
-
- ramfs_fs_type.name = "ramfs";
- ramfs_fs_type.fs_flags = FS_NO_DEVICE;
- ramfs_fs_type.mount = ramfs_mount;
- ramfs_fs_type.kill_sb = ramfs_kill_sb;
- list_init(&ramfs_fs_type.fs_list);
-
- register_filesystem(&ramfs_fs_type);
-}
-
diff --git a/na/fs/super.cc b/na/fs/super.cc
deleted file mode 100644
index 231b57c..0000000
--- a/na/fs/super.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * super.cc — Superblock lifecycle + filesystem type registry
- */
-
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/mount.h>
-#include <lume/klog.h>
-#include <lume/slab.h>
-#include <lume/errno.h>
-
-/* Global root filesystem entry points */
-vfsmount* g_root_mnt = nullptr;
-dentry* g_root_dentry = nullptr;
-
-/* ================================================================== */
-/* Filesystem type registry */
-/* ================================================================== */
-
-namespace {
-
-list_head g_fs_types;
-Spinlock g_fs_type_lock;
-
-} // namespace
-
-int register_filesystem(file_system_type* fs)
-{
- LockGuard guard(g_fs_type_lock);
- list_add_tail(&fs->fs_list, &g_fs_types);
- printk("[vfs] registered filesystem: %s\n", fs->name);
- return 0;
-}
-
-file_system_type* get_fs_type(const char* name)
-{
- LockGuard guard(g_fs_type_lock);
- list_node* pos;
- list_for_each(pos, &g_fs_types) {
- auto* fs = list_entry(pos, file_system_type, fs_list);
- /* Simple strcmp */
- const char* a = fs->name;
- const char* b = name;
- while (*a && *a == *b) { a++; b++; }
- if (*a == '\0' && *b == '\0')
- return fs;
- }
- return nullptr;
-}
-
-/* ================================================================== */
-/* Superblock allocation */
-/* ================================================================== */
-
-super_block* alloc_super(file_system_type* type)
-{
- auto* sb = new super_block{};
- if (!sb) return nullptr;
-
- list_init(&sb->s_list);
- list_init(&sb->s_inodes);
- sb->s_type = type;
- sb->s_op = nullptr;
- sb->s_root = nullptr;
- sb->s_fs_info = nullptr;
- sb->s_blocksize = 4096;
- sb->s_blocksize_bits = 12;
- sb->s_maxbytes = 0x7FFFFFFFFFFFFFFFLL;
- sb->s_time_gran = 1;
- sb->s_time_min = 0;
- sb->s_time_max = 0x7FFFFFFFFFFFFFFFLL;
- sb->s_flags = 0;
- sb->s_magic = 0;
- sb->s_active.store(1);
- sb->s_count = 1;
- sb->s_max_links = 0;
- sb->s_d_op = nullptr;
- sb->s_mounts = nullptr;
- sb->s_dev = 0;
- for (int i = 0; i < 32; i++) sb->s_id[i] = 0;
-
- return sb;
-}
-
-/* ================================================================== */
-/* Init */
-/* ================================================================== */
-
-void super_init()
-{
- list_init(&g_fs_types);
-}
diff --git a/na/fs/vfs_init.cc b/na/fs/vfs_init.cc
deleted file mode 100644
index ec56e88..0000000
--- a/na/fs/vfs_init.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * vfs_init.cc — Bootstrap the VFS: mount ramfs root, set global root
- *
- * Called from kernel_main() after slab_init() and ramfs_init().
- * This is the moral equivalent of Linux's vfs_caches_init() +
- * mnt_init() + init_rootfs().
- */
-
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/file.h>
-#include <lume/mount.h>
-#include <lume/fdtable.h>
-#include <lume/fs_struct.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-
-/* ================================================================== */
-/* vfs_init — mount ramfs as root filesystem */
-/* ================================================================== */
-
-/*
- * ramfs_mount is called internally via the file_system_type::mount
- * callback registered by ramfs_init(). We look it up by name.
- */
-extern file_system_type* get_fs_type(const char* name);
-extern super_block* alloc_super(file_system_type* type);
-
-void vfs_init()
-{
- /* 1. Look up the ramfs filesystem type */
- file_system_type* ramfs_type = get_fs_type("ramfs");
- if (!ramfs_type || !ramfs_type->mount) {
- printk(KERN_ERR "vfs_init: ramfs not registered!\n");
- return;
- }
-
- /* 2. Mount ramfs */
- super_block* sb = ramfs_type->mount(ramfs_type, 0, "ramfs", nullptr);
- if (!sb || !sb->s_root) {
- printk(KERN_ERR "vfs_init: failed to mount ramfs!\n");
- return;
- }
-
- /* 3. Set global root */
- g_root_dentry = sb->s_root;
- g_root_mnt = nullptr; /* TODO: allocate a proper vfsmount when mount.cc lands */
-
- dget(g_root_dentry); /* Hold a reference for the global root */
-
- printk(KERN_INFO "[vfs] root filesystem mounted (ramfs)\n");
-
- /* 4. Set up VFS state for the current (BSP idle) task,
- * which will be inherited by all subsequently created tasks. */
- TaskControlBlock* bsp = current_task();
- if (bsp) {
- if (!bsp->files) {
- bsp->files = alloc_files_struct();
- }
- if (!bsp->fs) {
- bsp->fs = alloc_fs_struct();
- if (bsp->fs) {
- bsp->fs->root.de = g_root_dentry;
- bsp->fs->root.mnt = g_root_mnt;
- bsp->fs->pwd.de = g_root_dentry;
- bsp->fs->pwd.mnt = g_root_mnt;
- dget(g_root_dentry); /* root ref */
- dget(g_root_dentry); /* pwd ref */
- }
- }
- printk(KERN_INFO "[vfs] BSP task VFS context initialized\n");
- }
-}
diff --git a/na/include/libc-shim/bits/alltypes.h b/na/include/libc-shim/bits/alltypes.h
deleted file mode 100644
index cf21a5e..0000000
--- a/na/include/libc-shim/bits/alltypes.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Shim: provide types musl string functions need */
-#ifndef _BITS_ALLTYPES_H
-#define _BITS_ALLTYPES_H
-
-#ifndef __cplusplus
-typedef unsigned long size_t;
-#endif
-
-typedef long ssize_t;
-typedef unsigned long uintptr_t;
-
-/* locale_t stub — unused in kernel, but declared by some headers */
-typedef void *locale_t;
-
-#endif
diff --git a/na/include/libc-shim/endian.h b/na/include/libc-shim/endian.h
deleted file mode 100644
index 8346386..0000000
--- a/na/include/libc-shim/endian.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Shim: endianness for musl string functions */
-#ifndef _ENDIAN_H
-#define _ENDIAN_H
-
-#define __LITTLE_ENDIAN 1234
-#define __BIG_ENDIAN 4321
-#define __BYTE_ORDER __LITTLE_ENDIAN /* RISC-V is LE */
-
-#endif
diff --git a/na/include/libc-shim/features.h b/na/include/libc-shim/features.h
deleted file mode 100644
index a3466c8..0000000
--- a/na/include/libc-shim/features.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Shim: musl expects this header */
-#ifndef _FEATURES_H
-#define _FEATURES_H
-
-/* weak_alias: musl uses this for symbol aliasing (e.g. __memrchr -> memrchr) */
-#define weak_alias(old, new) \
- extern __typeof(old) new __attribute__((__weak__, __alias__(#old)))
-
-/* hidden attribute used by some musl sources */
-#define hidden __attribute__((__visibility__("hidden")))
-
-#endif
diff --git a/na/include/libc-shim/limits.h b/na/include/libc-shim/limits.h
deleted file mode 100644
index a1d546e..0000000
--- a/na/include/libc-shim/limits.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* Shim: limits for musl string functions (memmem uses SIZE_MAX) */
-#ifndef _LIMITS_H
-#define _LIMITS_H
-
-#define CHAR_BIT 8
-#define UCHAR_MAX 255
-#define SCHAR_MAX 127
-#define SCHAR_MIN (-128)
-#define INT_MAX 2147483647
-#define INT_MIN (-INT_MAX - 1)
-#define UINT_MAX 4294967295U
-#define LONG_MAX 9223372036854775807L
-#define LONG_MIN (-LONG_MAX - 1L)
-#define ULONG_MAX 18446744073709551615UL
-#define SIZE_MAX ULONG_MAX
-
-#endif
diff --git a/na/include/libc-shim/stdint.h b/na/include/libc-shim/stdint.h
deleted file mode 100644
index 650bdcf..0000000
--- a/na/include/libc-shim/stdint.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Shim: stdint types for musl string functions */
-#ifndef _STDINT_H
-#define _STDINT_H
-
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-typedef unsigned long uint64_t;
-typedef signed char int8_t;
-typedef signed short int16_t;
-typedef signed int int32_t;
-typedef signed long int64_t;
-typedef unsigned long uintptr_t;
-typedef long intptr_t;
-
-#define UINT8_MAX 0xFF
-#define UINT16_MAX 0xFFFF
-#define UINT32_MAX 0xFFFFFFFFU
-#define UINT64_MAX 0xFFFFFFFFFFFFFFFFUL
-
-#endif
diff --git a/na/include/libc-shim/string.h b/na/include/libc-shim/string.h
deleted file mode 100644
index 8a86304..0000000
--- a/na/include/libc-shim/string.h
+++ /dev/null
@@ -1,110 +0,0 @@
-#ifndef _STRING_H
-#define _STRING_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <features.h>
-
-#if __cplusplus >= 201103L
-#define NULL nullptr
-#elif defined(__cplusplus)
-#define NULL 0L
-#else
-#define NULL ((void*)0)
-#endif
-
-#define __NEED_size_t
-#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
- || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
- || defined(_BSD_SOURCE)
-#define __NEED_locale_t
-#endif
-
-#include <bits/alltypes.h>
-
-void *memcpy (void *__restrict, const void *__restrict, size_t);
-void *memmove (void *, const void *, size_t);
-void *memset (void *, int, size_t);
-int memcmp (const void *, const void *, size_t);
-void *memchr (const void *, int, size_t);
-
-char *strcpy (char *__restrict, const char *__restrict);
-char *strncpy (char *__restrict, const char *__restrict, size_t);
-
-char *strcat (char *__restrict, const char *__restrict);
-char *strncat (char *__restrict, const char *__restrict, size_t);
-
-int strcmp (const char *, const char *);
-int strncmp (const char *, const char *, size_t);
-
-int strcoll (const char *, const char *);
-size_t strxfrm (char *__restrict, const char *__restrict, size_t);
-
-char *strchr (const char *, int);
-char *strrchr (const char *, int);
-
-size_t strcspn (const char *, const char *);
-size_t strspn (const char *, const char *);
-char *strpbrk (const char *, const char *);
-char *strstr (const char *, const char *);
-char *strtok (char *__restrict, const char *__restrict);
-
-size_t strlen (const char *);
-
-char *strerror (int);
-
-#if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
-#include <strings.h>
-#endif
-
-#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
- || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
- || defined(_BSD_SOURCE)
-char *strtok_r (char *__restrict, const char *__restrict, char **__restrict);
-int strerror_r (int, char *, size_t);
-char *stpcpy(char *__restrict, const char *__restrict);
-char *stpncpy(char *__restrict, const char *__restrict, size_t);
-size_t strnlen (const char *, size_t);
-char *strdup (const char *);
-char *strndup (const char *, size_t);
-char *strsignal(int);
-char *strerror_l (int, locale_t);
-int strcoll_l (const char *, const char *, locale_t);
-size_t strxfrm_l (char *__restrict, const char *__restrict, size_t, locale_t);
-void *memmem(const void *, size_t, const void *, size_t);
-#endif
-
-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
- || defined(_BSD_SOURCE)
-void *memccpy (void *__restrict, const void *__restrict, int, size_t);
-#endif
-
-#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
-char *strsep(char **, const char *);
-size_t strlcat (char *, const char *, size_t);
-size_t strlcpy (char *, const char *, size_t);
-void explicit_bzero (void *, size_t);
-#endif
-
-#ifdef _GNU_SOURCE
-#define strdupa(x) strcpy(alloca(strlen(x)+1),x)
-int strverscmp (const char *, const char *);
-char *strchrnul(const char *, int);
-char *strcasestr(const char *, const char *);
-void *memrchr(const void *, int, size_t);
-void *mempcpy(void *, const void *, size_t);
-#endif
-
-/* musl internal symbols used across translation units */
-char *__strchrnul(const char *, int);
-void *__memrchr(const void *, int, size_t);
-char *__stpcpy(char *__restrict, const char *__restrict);
-char *__stpncpy(char *__restrict, const char *__restrict, size_t);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/na/include/libc-shim/strings.h b/na/include/libc-shim/strings.h
deleted file mode 100644
index b7a5ea0..0000000
--- a/na/include/libc-shim/strings.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef _STRINGS_H
-#define _STRINGS_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <features.h>
-
-#define __NEED_size_t
-#define __NEED_locale_t
-#include <bits/alltypes.h>
-
-#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) || defined(_POSIX_SOURCE) \
- || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE+0 < 200809L) \
- || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE+0 < 700)
-int bcmp (const void *, const void *, size_t);
-void bcopy (const void *, void *, size_t);
-void bzero (void *, size_t);
-char *index (const char *, int);
-char *rindex (const char *, int);
-#endif
-
-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
-int ffs (int);
-int ffsl (long);
-int ffsll (long long);
-#endif
-
-int strcasecmp (const char *, const char *);
-int strncasecmp (const char *, const char *, size_t);
-
-int strcasecmp_l (const char *, const char *, locale_t);
-int strncasecmp_l (const char *, const char *, size_t, locale_t);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/na/include/lume/addr.h b/na/include/lume/addr.h
deleted file mode 100644
index 572b6fe..0000000
--- a/na/include/lume/addr.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Machine-Independent Address Conversion Helpers
- *
- * All PA↔VA conversions in MI code (mm/, kernel/, etc.) must go through
- * these functions. The actual offset is provided by <arch/config.h>.
- *
- * Reference: docs/specs/00_conventions.md §1.1 (MI/MD separation)
- */
-
-#include <lume/types.h>
-#include <lume/config.h>
-#include <lume/addr_types.h>
-#include <arch/config.h>
-
-/* Convert physical address to kernel virtual address (direct map). */
-inline VirtAddr pa_to_va(PhysAddr pa)
-{
- return VirtAddr{pa.raw + arch::g_direct_map_base};
-}
-
-/* Convert kernel virtual address to physical address (direct map). */
-inline PhysAddr va_to_pa(VirtAddr va)
-{
- return PhysAddr{va.raw - arch::g_direct_map_base};
-}
-
-/*
- * Early-boot conversions pinned to the bootstrap direct-map base.
- *
- * Use these only before vmm_init() activates the final kernel page table.
- * They are required when KASLR has already randomized g_direct_map_base,
- * but the CPU is still running on the bootstrap mapping built in entry.S.
- */
-inline VirtAddr boot_pa_to_va(PhysAddr pa)
-{
- return VirtAddr{pa.raw + arch::kDirectMapBaseDefault};
-}
-
-inline PhysAddr boot_va_to_pa(VirtAddr va)
-{
- return PhysAddr{va.raw - arch::kDirectMapBaseDefault};
-}
-
-/* Align up to page boundary */
-inline uint64 page_align_up(uint64 addr)
-{
- return (addr + kPageSize - 1) & ~(kPageSize - 1);
-}
-
-/* Align down to page boundary */
-inline uint64 page_align_down(uint64 addr)
-{
- return addr & ~(kPageSize - 1);
-}
-
-inline PhysAddr page_align_up(PhysAddr addr)
-{
- return PhysAddr{page_align_up(addr.raw)};
-}
-
-inline PhysAddr page_align_down(PhysAddr addr)
-{
- return PhysAddr{page_align_down(addr.raw)};
-}
-
-inline VirtAddr page_align_up(VirtAddr addr)
-{
- return VirtAddr{page_align_up(addr.raw)};
-}
-
-inline VirtAddr page_align_down(VirtAddr addr)
-{
- return VirtAddr{page_align_down(addr.raw)};
-}
-
-/* Convert physical address to page frame number (PFN) */
-inline Pfn pa_to_pfn(PhysAddr pa)
-{
- return Pfn{pa.raw / kPageSize};
-}
-
-/* Convert page frame number (PFN) to physical address */
-inline PhysAddr pfn_to_pa(Pfn pfn)
-{
- return PhysAddr{pfn.raw * kPageSize};
-}
diff --git a/na/include/lume/addr_types.h b/na/include/lume/addr_types.h
deleted file mode 100644
index 91a0779..0000000
--- a/na/include/lume/addr_types.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-#include <lume/types.h>
-
-struct PhysAddr {
- uint64 raw;
-};
-
-struct VirtAddr {
- uint64 raw;
-};
-
-struct Pfn {
- uint64 raw;
-};
-
-inline constexpr PhysAddr phys_addr(uint64 raw) { return PhysAddr{raw}; }
-inline constexpr VirtAddr virt_addr(uint64 raw) { return VirtAddr{raw}; }
-inline constexpr Pfn pfn(uint64 raw) { return Pfn{raw}; }
-
-inline constexpr bool operator==(PhysAddr a, PhysAddr b) { return a.raw == b.raw; }
-inline constexpr bool operator!=(PhysAddr a, PhysAddr b) { return a.raw != b.raw; }
-inline constexpr bool operator==(VirtAddr a, VirtAddr b) { return a.raw == b.raw; }
-inline constexpr bool operator!=(VirtAddr a, VirtAddr b) { return a.raw != b.raw; }
-inline constexpr bool operator==(Pfn a, Pfn b) { return a.raw == b.raw; }
-inline constexpr bool operator!=(Pfn a, Pfn b) { return a.raw != b.raw; }
diff --git a/na/include/lume/args.h b/na/include/lume/args.h
deleted file mode 100644
index 0562dc5..0000000
--- a/na/include/lume/args.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-
-#ifndef _LINUX_ARGS_H
-#define _LINUX_ARGS_H
-
-/*
- * How do these macros work?
- *
- * In __COUNT_ARGS() _0 to _15 are just placeholders from the start
- * in order to make sure _n is positioned over the correct number
- * from 15 to 0 (depending on X, which is a variadic argument list).
- * They serve no purpose other than occupying a position. Since each
- * macro parameter must have a distinct identifier, those identifiers
- * are as good as any.
- *
- * In COUNT_ARGS() we use actual integers, so __COUNT_ARGS() returns
- * that as _n.
- */
-
-/* This counts to 15. Any more, it will return 16th argument. */
-#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _n, X...) _n
-#define COUNT_ARGS(X...) __COUNT_ARGS(, ##X, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
-
-/* Concatenate two parameters, but allow them to be expanded beforehand. */
-#define __CONCAT(a, b) a ## b
-#define CONCATENATE(a, b) __CONCAT(a, b)
-
-#endif /* _LINUX_ARGS_H */
diff --git a/na/include/lume/atomic.h b/na/include/lume/atomic.h
deleted file mode 100644
index 6e31129..0000000
--- a/na/include/lume/atomic.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * lume::atomic<T> — Thin wrapper over __atomic builtins.
- *
- * All multi-core shared data that does NOT use a spinlock MUST use
- * this wrapper instead of raw __atomic_* builtins.
- *
- * Reference: docs/specs/00_conventions.md §5.3
- */
-
-#include <lume/types.h>
-
-namespace lume {
-
-template<typename T>
-class atomic {
-public:
- atomic() = default;
- explicit atomic(T val) : val_(val) {}
-
- /* Prevent copies — an atomic value has identity */
- atomic(const atomic&) = delete;
- atomic& operator=(const atomic&) = delete;
-
- /* Load with specified memory ordering */
- T load(int order = __ATOMIC_SEQ_CST) const {
- return __atomic_load_n(&val_, order);
- }
-
- /* Store with specified memory ordering */
- void store(T desired, int order = __ATOMIC_SEQ_CST) {
- __atomic_store_n(&val_, desired, order);
- }
-
- /* Atomic exchange, returns old value */
- T exchange(T desired, int order = __ATOMIC_SEQ_CST) {
- return __atomic_exchange_n(&val_, desired, order);
- }
-
- /* Fetch-and-add, returns old value */
- T fetch_add(T delta, int order = __ATOMIC_ACQ_REL) {
- return __atomic_fetch_add(&val_, delta, order);
- }
-
- /* Fetch-and-subtract, returns old value */
- T fetch_sub(T delta, int order = __ATOMIC_ACQ_REL) {
- return __atomic_fetch_sub(&val_, delta, order);
- }
-
- /* Fetch-and-or, returns old value */
- T fetch_or(T mask, int order = __ATOMIC_ACQ_REL) {
- return __atomic_fetch_or(&val_, mask, order);
- }
-
- /* Fetch-and-and, returns old value */
- T fetch_and(T mask, int order = __ATOMIC_ACQ_REL) {
- return __atomic_fetch_and(&val_, mask, order);
- }
-
- /* Compare-and-swap. Returns true on success. */
- bool compare_exchange(T& expected, T desired,
- int success_order = __ATOMIC_ACQ_REL,
- int failure_order = __ATOMIC_ACQUIRE) {
- return __atomic_compare_exchange_n(&val_, &expected, desired,
- false, success_order, failure_order);
- }
-
-private:
- T val_ = 0;
-};
-
-} // namespace lume
diff --git a/na/include/lume/bitops.h b/na/include/lume/bitops.h
deleted file mode 100644
index 4227ca3..0000000
--- a/na/include/lume/bitops.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Bit Operations — portable kernel bit manipulation utilities
- *
- * Provides ctz, clz, ffs, fls and related operations.
- * All implementations are manual (no __builtin_ctz) to avoid
- * generating libgcc __ctzdi2/__clzdi2 calls in freestanding.
- */
-
-#include <lume/types.h>
-
-namespace lume {
-
-/* ------------------------------------------------------------------ */
-/* Count Trailing Zeros (ctz) */
-/* Returns the number of trailing 0-bits in val. */
-/* Precondition: val != 0 (undefined if val == 0). */
-/* ------------------------------------------------------------------ */
-
-static inline int ctz32(uint32 val)
-{
- int n = 0;
- if (!(val & 0x0000FFFFU)) { n += 16; val >>= 16; }
- if (!(val & 0x000000FFU)) { n += 8; val >>= 8; }
- if (!(val & 0x0000000FU)) { n += 4; val >>= 4; }
- if (!(val & 0x00000003U)) { n += 2; val >>= 2; }
- if (!(val & 0x00000001U)) { n += 1; }
- return n;
-}
-
-static inline int ctz64(uint64 val)
-{
- if (static_cast<uint32>(val) != 0) {
- return ctz32(static_cast<uint32>(val));
- }
- return 32 + ctz32(static_cast<uint32>(val >> 32));
-}
-
-/* ------------------------------------------------------------------ */
-/* Count Leading Zeros (clz) */
-/* Returns the number of leading 0-bits in val. */
-/* Precondition: val != 0 (undefined if val == 0). */
-/* ------------------------------------------------------------------ */
-
-static inline int clz32(uint32 val)
-{
- int n = 0;
- if (!(val & 0xFFFF0000U)) { n += 16; val <<= 16; }
- if (!(val & 0xFF000000U)) { n += 8; val <<= 8; }
- if (!(val & 0xF0000000U)) { n += 4; val <<= 4; }
- if (!(val & 0xC0000000U)) { n += 2; val <<= 2; }
- if (!(val & 0x80000000U)) { n += 1; }
- return n;
-}
-
-static inline int clz64(uint64 val)
-{
- if (static_cast<uint32>(val >> 32) != 0) {
- return clz32(static_cast<uint32>(val >> 32));
- }
- return 32 + clz32(static_cast<uint32>(val));
-}
-
-/* ------------------------------------------------------------------ */
-/* Find First Set (ffs) — 1-indexed, returns 0 if val == 0 */
-/* ------------------------------------------------------------------ */
-
-static inline int ffs32(uint32 val)
-{
- return val ? ctz32(val) + 1 : 0;
-}
-
-static inline int ffs64(uint64 val)
-{
- return val ? ctz64(val) + 1 : 0;
-}
-
-/* ------------------------------------------------------------------ */
-/* Find Last Set (fls) — 1-indexed, returns 0 if val == 0 */
-/* Returns the position of the highest set bit. */
-/* ------------------------------------------------------------------ */
-
-static inline int fls32(uint32 val)
-{
- return val ? 32 - clz32(val) : 0;
-}
-
-static inline int fls64(uint64 val)
-{
- return val ? 64 - clz64(val) : 0;
-}
-
-/* ------------------------------------------------------------------ */
-/* Order (log2) — returns floor(log2(val)), val must be > 0 */
-/* ------------------------------------------------------------------ */
-
-static inline int ilog2_32(uint32 val)
-{
- return fls32(val) - 1;
-}
-
-static inline int ilog2_64(uint64 val)
-{
- return fls64(val) - 1;
-}
-
-/* ------------------------------------------------------------------ */
-/* Power-of-two test */
-/* ------------------------------------------------------------------ */
-
-static inline bool is_power_of_2(uint64 val)
-{
- return val != 0 && (val & (val - 1)) == 0;
-}
-
-} /* namespace lume */
diff --git a/na/include/lume/chardev.h b/na/include/lume/chardev.h
deleted file mode 100644
index be81ab9..0000000
--- a/na/include/lume/chardev.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * chardev.h — Character device abstraction
- *
- * Minimal Linux-style cdev registry: each character device has a
- * major/minor number and a file_operations table.
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-
-struct file_operations;
-
-/* ================================================================== */
-/* MKDEV / MAJOR / MINOR */
-/* ================================================================== */
-
-inline constexpr uint32 MKDEV(uint32 ma, uint32 mi) {
- return (ma << 8) | (mi & 0xFF);
-}
-inline constexpr uint32 MAJOR(uint32 dev) { return dev >> 8; }
-inline constexpr uint32 MINOR(uint32 dev) { return dev & 0xFF; }
-
-/* ================================================================== */
-/* cdev — character device descriptor */
-/* ================================================================== */
-
-struct cdev {
- const char* name;
- uint32 dev; /* MKDEV(major, minor) */
- const file_operations* fops;
- list_node list; /* global cdev list */
-};
-
-/* ================================================================== */
-/* API */
-/* ================================================================== */
-
-void cdev_init();
-void cdev_register(cdev* dev);
-cdev* cdev_find(uint32 dev);
diff --git a/na/include/lume/clone.h b/na/include/lume/clone.h
deleted file mode 100644
index 156bae4..0000000
--- a/na/include/lume/clone.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Linux-compatible clone(2) flags.
- *
- * Only the flags relevant to current and near-future use are defined.
- * Reference: include/uapi/linux/sched.h
- */
-
-#include <lume/types.h>
-
-/* Signal mask stored in low byte of clone flags */
-inline constexpr uint64 CSIGNAL = 0x000000ffULL;
-
-/* Sharing flags */
-inline constexpr uint64 CLONE_VM = 0x00000100ULL; // Share address space
-inline constexpr uint64 CLONE_FS = 0x00000200ULL; // Share fs info
-inline constexpr uint64 CLONE_FILES = 0x00000400ULL; // Share file descriptors
-inline constexpr uint64 CLONE_SIGHAND = 0x00000800ULL; // Share signal handlers
-inline constexpr uint64 CLONE_PIDFD = 0x00001000ULL; // pidfd
-inline constexpr uint64 CLONE_PTRACE = 0x00002000ULL; // Trace child
-inline constexpr uint64 CLONE_VFORK = 0x00004000ULL; // Parent blocks until child exits/execs
-inline constexpr uint64 CLONE_PARENT = 0x00008000ULL; // Same parent as cloner
-inline constexpr uint64 CLONE_THREAD = 0x00010000ULL; // Same thread group
-inline constexpr uint64 CLONE_NEWNS = 0x00020000ULL; // New mount namespace
-inline constexpr uint64 CLONE_SYSVSEM = 0x00040000ULL; // Share SysV semaphore undo
-inline constexpr uint64 CLONE_SETTLS = 0x00080000ULL; // Set TLS
-inline constexpr uint64 CLONE_PARENT_SETTID = 0x00100000ULL; // Write TID to parent
-inline constexpr uint64 CLONE_CHILD_CLEARTID = 0x00200000ULL; // Clear TID on child exit
-inline constexpr uint64 CLONE_DETACHED = 0x00400000ULL; // Unused, historical
-inline constexpr uint64 CLONE_CHILD_SETTID = 0x01000000ULL; // Write TID to child
diff --git a/na/include/lume/compiler.h b/na/include/lume/compiler.h
deleted file mode 100644
index 243f0de..0000000
--- a/na/include/lume/compiler.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _LUME_COMPILER_H
-#define _LUME_COMPILER_H
-
-/*
- * Minimal compiler primitives used across LumeOS.
- *
- * Provides only what current code actually references:
- * - READ_ONCE / WRITE_ONCE memory access opacity for concurrent code
- * (rbtree.h, rbtree_augmented.h)
- * - likely / unlikely branch hints for hot/cold paths
- *
- * Implementation mirrors Linux's semantics — same surface, same effect —
- * without pulling in Linux's compiler_types.h cascade (KASAN/KCSAN/KMSAN
- * sanitizer attribute machinery, BTF type tags, hardened-usercopy hooks).
- * LumeOS doesn't build with those sanitizers, so the simpler form is
- * sufficient and keeps the dependency graph flat.
- */
-
-/* Branch prediction hints */
-#define likely(x) __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
-
-/* Compile-time barrier — prevent the compiler from reordering memory
- * accesses across this point. Not a CPU fence; for CPU ordering use
- * the architecture-specific fence intrinsics. */
-#define barrier() __asm__ __volatile__("" ::: "memory")
-
-/*
- * __always_inline — force the compiler to actually inline a function,
- * overriding -fno-inline / -Os / size heuristics. Used by rbtree helper
- * templates whose performance depends on the call being inlined into
- * the iterating loop.
- */
-#define __always_inline inline __attribute__((__always_inline__))
-
-/*
- * READ_ONCE / WRITE_ONCE — opaque single-access memory operations.
- *
- * The volatile cast prevents the compiler from:
- * - merging adjacent reads/writes to the same location,
- * - splitting a load into multiple narrower loads,
- * - hoisting reads out of a loop (or sinking writes past one).
- *
- * Used in lock-free / cross-CPU shared data paths (rbtree color writes,
- * future RCU pointers, atomic flag polls). Does NOT imply any CPU-side
- * memory ordering — pair with smp_mb()/acquire/release for that.
- */
-#define READ_ONCE(x) (*(const volatile __typeof__(x) *)&(x))
-#define WRITE_ONCE(x, val) (*(volatile __typeof__(x) *)&(x) = (val))
-
-#endif /* _LUME_COMPILER_H */
diff --git a/na/include/lume/config.h b/na/include/lume/config.h
deleted file mode 100644
index bde717a..0000000
--- a/na/include/lume/config.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * LumeOS Global Compile-Time Configuration
- *
- * All kernel-wide tuning constants live here.
- * Architecture-specific constants live in <arch/config.h>.
- *
- * This header is safe to include from both C++ and assembly files.
- * Assembly-visible constants use #define; C++ code gets typed constexpr.
- *
- * Values are injected by the build system via -DCONFIG_xxx flags
- * (see scripts/Kconfig.mk). The #ifndef guards below provide
- * safe defaults so the header is self-contained for IDE analysis.
- *
- * Reference: docs/specs/00_conventions.md §1.3
- */
-
-/* ================================================================
- * Defaults — only used when CONFIG_xxx is NOT supplied by -D flag.
- * In a normal build, scripts/Kconfig.mk always provides them.
- * ================================================================ */
-
-#ifndef CONFIG_NR_CPUS
-#define CONFIG_NR_CPUS 8
-#endif
-
-#ifndef CONFIG_PAGE_SIZE
-#define CONFIG_PAGE_SIZE 4096
-#endif
-
-#ifndef CONFIG_MAX_ORDER
-#define CONFIG_MAX_ORDER 11
-#endif
-
-#ifndef CONFIG_KERNEL_STACK_SIZE
-#define CONFIG_KERNEL_STACK_SIZE 16384
-#endif
-
-#ifndef CONFIG_HZ
-#define CONFIG_HZ 100
-#endif
-
-#ifndef CONFIG_NR_PRIORITIES
-#define CONFIG_NR_PRIORITIES 32
-#endif
-
-#ifndef CONFIG_DEFAULT_PRIORITY
-#define CONFIG_DEFAULT_PRIORITY (CONFIG_NR_PRIORITIES / 2)
-#endif
-
-#ifndef CONFIG_IDLE_PRIORITY
-#define CONFIG_IDLE_PRIORITY (CONFIG_NR_PRIORITIES - 1)
-#endif
-
-#ifndef CONFIG_DEFAULT_TIMESLICE
-#define CONFIG_DEFAULT_TIMESLICE (CONFIG_HZ / 10)
-#endif
-
-#ifndef CONFIG_KASLR_POLICY_ID
-#define CONFIG_KASLR_POLICY_ID 0 /* Default: basic KASLR policy */
-#endif
-
-#ifndef CONFIG_KASLR_LOG_LEVEL
-#define CONFIG_KASLR_LOG_LEVEL 1 /* 0=off, 1=minimal, 2=debug */
-#endif
-
-/* ================================================================
- * Legacy compatibility aliases (shared with assembly)
- * ================================================================ */
-#define LUME_MAX_CPUS CONFIG_NR_CPUS
-#define LUME_PAGE_SIZE CONFIG_PAGE_SIZE
-
-#ifndef __ASSEMBLER__
-
-#include <lume/types.h>
-
-/* Maximum number of CPUs supported by the kernel.
- * Compile-time limit for static arrays (boot stacks, per-cpu data).
- * Actual CPU count is detected at runtime via FDT. */
-inline constexpr int kMaxCpus = CONFIG_NR_CPUS;
-
-/* Virtual memory page size (4KB, matching SV39 base page). */
-inline constexpr uint64 kPageSize = CONFIG_PAGE_SIZE;
-
-/* Page shift: log2(kPageSize). Used for PFN ↔ PA conversions. */
-inline constexpr int kPageShift = 12; // 4096 = 1 << 12
-
-/* KASLR policy ID and logging level (build-time configurable) */
-inline constexpr uint32 kKaslrPolicyId = CONFIG_KASLR_POLICY_ID;
-inline constexpr int kKaslrLogLevel = CONFIG_KASLR_LOG_LEVEL;
-
-#include <arch/config.h>
-
-using arch::kPhysStart;
-
-#endif /* __ASSEMBLER__ */ \ No newline at end of file
diff --git a/na/include/lume/console.h b/na/include/lume/console.h
deleted file mode 100644
index af187a6..0000000
--- a/na/include/lume/console.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * console.h — Kernel console abstraction
- *
- * Two-phase design following Linux:
- *
- * Phase 1 (early boot):
- * early_putc() → direct UART MMIO write. Used before MMU and
- * before the console registry is ready. kprintf() and panic
- * use this path.
- *
- * Phase 2 (runtime):
- * struct console registered via register_console(). printk/klog
- * emit through the console driver chain. Multiple consoles can
- * be registered (e.g., UART + framebuffer).
- *
- * Reference: Linux include/linux/console.h
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-
-/* ================================================================== */
-/* Early console (Phase 1 — direct UART access) */
-/* ================================================================== */
-
-extern "C" void early_putc(char c);
-extern "C" void early_puts(const char* s);
-extern "C" void console_set_mmu_enabled();
-
-/* Early UART discovery via raw FDT parsing. */
-void console_early_init(uint64 fdt_paddr);
-
-/* Full console initialization. Maps MMIO. */
-void console_init();
-
-/*
- * Notify console that the runtime kernel page table is active.
- * After this point early_putc() must use runtime direct-map base
- * instead of the bootstrap fixed base.
- */
-void console_use_runtime_mapping();
-
-/* ================================================================== */
-/* struct console (Phase 2 — registered console drivers) */
-/* ================================================================== */
-
-inline constexpr int CON_PRINTBUFFER = (1 << 0); /* Replay klog on register */
-inline constexpr int CON_ENABLED = (1 << 1);
-inline constexpr int CON_BOOT = (1 << 2); /* Early boot console */
-
-struct console {
- const char* name;
- void (*write)(console* con, const char* s, unsigned int count);
- int (*setup)(console* con, char* options);
- int flags;
- int index;
- void* data; /* driver private data */
- list_node list;
-};
-
-/* Register / unregister a console driver */
-void register_console(console* con);
-void unregister_console(console* con);
-
-/*
- * Write to all registered consoles.
- * Falls back to early_putc() if no console is registered.
- */
-void console_write_all(const char* s, int count);
-
-/* Check if any console is registered */
-bool console_drivers_registered();
diff --git a/na/include/lume/container_of.h b/na/include/lume/container_of.h
deleted file mode 100644
index a6889eb..0000000
--- a/na/include/lume/container_of.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * container_of.h — Recover containing struct from member pointer
- *
- * Equivalent to Linux include/linux/container_of.h.
- */
-
-#include <lume/types.h>
-
-#define container_of(ptr, type, member) \
- reinterpret_cast<type*>( \
- reinterpret_cast<uintptr>(ptr) - \
- reinterpret_cast<uintptr>(&(reinterpret_cast<type*>(0)->member)))
diff --git a/na/include/lume/dcache.h b/na/include/lume/dcache.h
deleted file mode 100644
index a31402e..0000000
--- a/na/include/lume/dcache.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * dcache.h — Directory entry cache
- *
- * Modeled after Linux include/linux/dcache.h.
- * Reference: docs/vfs/03_dentry.md
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <lume/atomic.h>
-#include <lume/fs_types.h>
-#include <kernel/sync/spinlock.h>
-
-struct inode;
-struct super_block;
-struct dentry;
-struct dentry_operations;
-struct path;
-
-/* ================================================================== */
-/* qstr — quick string (name + precomputed hash + length) */
-/* ================================================================== */
-
-struct qstr {
- union {
- struct {
- uint32 hash;
- uint32 len;
- };
- uint64 hash_len;
- };
- const char* name;
-};
-
-uint32 full_name_hash(const char* name, uint32 len);
-
-/* ================================================================== */
-/* dentry */
-/* ================================================================== */
-
-inline constexpr int DNAME_INLINE_LEN = 40;
-
-struct dentry {
- /* ---- RCU / lookup hot fields ---- */
- unsigned int d_flags;
- dentry* d_parent;
- qstr d_name;
- inode* d_inode;
- unsigned char d_iname[DNAME_INLINE_LEN];
-
- /* ---- Ref lookup ---- */
- const dentry_operations* d_op;
- super_block* d_sb;
- unsigned long d_time;
- void* d_fsdata;
-
- /* ---- Refcount + lock ---- */
- lume::atomic<int32> d_lockref;
- Spinlock d_lock;
-
- /* ---- Tree linkage ---- */
- list_node d_lru;
- list_node d_child; /* Linkage in parent->d_children */
- list_head d_children; /* Children of this dentry */
-
- /* ---- Inode alias ---- */
- list_node d_alias; /* Linkage in inode->i_dentry */
-};
-
-/* ================================================================== */
-/* dentry_operations */
-/* ================================================================== */
-
-struct dentry_operations {
- int (*d_revalidate)(dentry* de, unsigned int flags);
- int (*d_hash)(const dentry* de, qstr* name);
- int (*d_compare)(const dentry* parent, unsigned int len,
- const char* str, const qstr* name);
- int (*d_delete)(const dentry* de);
- int (*d_init)(dentry* de);
- void (*d_release)(dentry* de);
- void (*d_iput)(dentry* de, inode* ip);
-};
-
-/* ================================================================== */
-/* Dentry helpers */
-/* ================================================================== */
-
-inline bool d_is_negative(const dentry* de) {
- return (de->d_flags & DCACHE_ENTRY_TYPE) == DCACHE_MISS_TYPE;
-}
-inline bool d_is_positive(const dentry* de) {
- return !d_is_negative(de);
-}
-inline bool d_is_dir(const dentry* de) {
- return (de->d_flags & DCACHE_ENTRY_TYPE) == DCACHE_DIRECTORY_TYPE;
-}
-inline bool d_is_reg(const dentry* de) {
- return (de->d_flags & DCACHE_ENTRY_TYPE) == DCACHE_REGULAR_TYPE;
-}
-inline bool d_is_symlink(const dentry* de) {
- return (de->d_flags & DCACHE_ENTRY_TYPE) == DCACHE_SYMLINK_TYPE;
-}
-inline bool d_mountpoint(const dentry* de) {
- return (de->d_flags & DCACHE_MOUNTED) != 0;
-}
-inline bool d_is_root(const dentry* de) {
- return de == de->d_parent;
-}
-
-inline inode* d_inode_ptr(const dentry* de) {
- return de->d_inode;
-}
-
-/* ================================================================== */
-/* Dcache API */
-/* ================================================================== */
-
-dentry* d_alloc(dentry* parent, const qstr* name);
-dentry* d_alloc_anon(super_block* sb);
-void d_instantiate(dentry* de, inode* ip);
-dentry* d_make_root(inode* root_inode);
-void d_add(dentry* de, inode* ip);
-dentry* d_lookup(const dentry* parent, const qstr* name);
-dentry* dget(dentry* de);
-void dput(dentry* de);
-void d_drop(dentry* de);
-void d_delete(dentry* de);
-char* d_path(const path* p, char* buf, int buflen);
-
-/* Set dentry type flags based on inode mode */
-void d_set_type(dentry* de, uint32 i_mode);
-
-/* ================================================================== */
-/* Dcache init */
-/* ================================================================== */
-
-void dcache_init();
diff --git a/na/include/lume/device.h b/na/include/lume/device.h
deleted file mode 100644
index 46e22e4..0000000
--- a/na/include/lume/device.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Driver model: device, device_driver, bus_type.
- *
- * Mirrors Linux include/linux/device.h. Every hardware unit embeds a
- * `device` and binds to a `device_driver` via a `bus_type` that knows
- * how to match the two. Buses we will have:
- * - platform: FDT-described memory-mapped devices (this commit)
- * - virtio: virtio-mmio devices (P5 of storage_stack.md)
- *
- * Reference: docs/specs/storage_stack.md §4.3
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <lume/kobject.h>
-#include <kernel/sync/spinlock.h>
-
-struct bus_type;
-struct device_driver;
-
-struct device {
- kobject kobj; // embedded; first member by convention
- device* parent;
- const char* init_name; // used until kobj.name is set
- bus_type* bus;
- device_driver* driver;
- void* driver_data;
- void* platform_data;
- uint32 devt; // for char/block dev_t allocation
- list_node bus_list; // node in bus->devices
- list_node driver_list; // node in driver->devices
-};
-
-struct device_driver {
- const char* name;
- bus_type* bus;
- int (*probe)(device*);
- void (*remove)(device*);
- list_node bus_node; // node in bus->drivers
- list_node devices; // head of devices bound to this driver
-};
-
-struct bus_type {
- const char* name;
- int (*match)(device*, device_driver*);
- /* If non-null, this is the bus-level probe dispatcher. It is called
- * before drv->probe and is responsible for downcasting device/driver
- * to the concrete bus type and forwarding (e.g. platform_dispatch). */
- int (*probe)(device*);
- list_node devices;
- list_node drivers;
- Spinlock lock;
- bool registered;
-};
-
-int bus_register (bus_type* bus);
-
-/* device_initialize: kobject_init the embedded kobj with the generic
- * device_ktype. device_register calls this automatically if the caller
- * hasn't already. Separate entry point for callers that want a half-
- * initialized device (so they can take refs before adding). */
-void device_initialize(device* dev);
-int device_register (device* dev);
-void device_unregister(device* dev);
-
-int driver_register (device_driver* drv);
-void driver_unregister(device_driver* drv);
-
-/* Convenience accessor — name from kobject if set, else init_name. */
-const char* dev_name(const device* dev);
diff --git a/na/include/lume/device_node.h b/na/include/lume/device_node.h
deleted file mode 100644
index e35f22d..0000000
--- a/na/include/lume/device_node.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * DeviceNode — FDT object tree node
- *
- * Each DeviceNode represents a single node in the unflattened device tree.
- * Nodes form a parent-children hierarchy via intrusive linked lists.
- * After unflattening, the tree is globally read-only and requires no locks.
- *
- * Created when Slab is available.
- *
- * Reference: docs/specs/fdt.md §3.2
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <lume/fdt_prop.h>
-
-class DeviceNode {
-public:
- // Construction / destruction (used by FdtManager::unflatten)
- DeviceNode(DeviceNode *parent, const char *name);
- ~DeviceNode();
-
- // --- Property access ---
-
- // Look up a property by name. Returns nullptr if not found.
- const FdtProperty *get_prop(const char *name) const;
-
- // Convenience helpers that decode the property value.
- // Return default_val when the property does not exist.
- uint32 get_prop_u32(const char *name, uint32 default_val = 0) const;
- uint64 get_prop_u64(const char *name, uint64 default_val = 0) const;
- const char *get_prop_string(const char *name) const;
-
- // --- Child traversal ---
-
- // Find a direct child by name. Returns nullptr if not found.
- DeviceNode *find_child(const char *name);
-
- // Invoke callback for every direct child.
- void for_each_child(void (*callback)(DeviceNode *));
-
- // --- Internal (used by FdtManager to build the tree) ---
- void add_child(DeviceNode *child);
- void add_prop(FdtProperty *prop);
-
- // --- Accessors for tree traversal ---
- const char *get_name() const { return name_; }
- list_node *children_list_head() { return children_list_.next; }
- list_node *children_list_sentinel() { return &children_list_; }
-
- // sibling_link_ offset needed by container_of in fdt.cc
- list_node sibling_link_; // Link in parent's children_list_
-
-private:
- const char *name_; // Node name (e.g. "memory@80000000")
- DeviceNode *parent_; // Parent node (nullptr for root)
- list_node children_list_; // Head of the children linked list
- list_node props_list_; // Head of the property linked list
-};
diff --git a/na/include/lume/driver.h b/na/include/lume/driver.h
deleted file mode 100644
index a8bd787..0000000
--- a/na/include/lume/driver.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * driver.h — boot-time driver probing entry point.
- *
- * The actual driver-model primitives (kobject, device, bus_type,
- * platform_device, platform_driver) live in their own headers:
- * <lume/kobject.h>, <lume/device.h>, <lume/platform_device.h>.
- *
- * This header just exposes the single boot entry point that
- * kernel_main() calls after the slab allocator + FDT unflatten are
- * ready. The implementation lives in drivers/base/platform.cc.
- *
- * Reference: docs/specs/storage_stack.md §4
- */
-
-/* Register the platform bus, register every linker-set platform_driver,
- * walk the FDT, and instantiate a platform_device for each compatible
- * match. Each device is bound to its driver via the platform bus
- * match/probe path. Returns after all probes have run. */
-void driver_probe_all();
diff --git a/na/include/lume/elf.h b/na/include/lume/elf.h
deleted file mode 100644
index 772ad97..0000000
--- a/na/include/lume/elf.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * ELF64 Type Definitions — Minimal set for loading executables
- * AND for the kernel's own .rela.dyn relocation walk at boot.
- *
- * Follows the ELF specification (Tool Interface Standard, TIS).
- * Only includes what's needed for static ELF64 executable loading.
- *
- * Reference: https://refspecs.linuxfoundation.org/elf/elf.pdf
- */
-
-#include <lume/types.h>
-
-/* ================================================================== */
-/* Basic ELF types */
-/* ================================================================== */
-
-using Elf64_Addr = uint64;
-using Elf64_Off = uint64;
-using Elf64_Half = uint16;
-using Elf64_Word = uint32;
-using Elf64_Sword = int32;
-using Elf64_Xword = uint64;
-using Elf64_Sxword = int64;
-
-/* ================================================================== */
-/* ELF Header */
-/* ================================================================== */
-
-inline constexpr int EI_NIDENT = 16;
-
-struct Elf64_Ehdr {
- uint8 e_ident[EI_NIDENT]; /* ELF identification */
- Elf64_Half e_type; /* Object file type */
- Elf64_Half e_machine; /* Machine type */
- Elf64_Word e_version; /* Object file version */
- Elf64_Addr e_entry; /* Entry point address */
- Elf64_Off e_phoff; /* Program header table offset */
- Elf64_Off e_shoff; /* Section header table offset */
- Elf64_Word e_flags; /* Processor-specific flags */
- Elf64_Half e_ehsize; /* ELF header size */
- Elf64_Half e_phentsize; /* Program header entry size */
- Elf64_Half e_phnum; /* Number of program headers */
- Elf64_Half e_shentsize; /* Section header entry size */
- Elf64_Half e_shnum; /* Number of section headers */
- Elf64_Half e_shstrndx; /* Section name string table index */
-};
-
-/* e_ident[] indices */
-inline constexpr int EI_MAG0 = 0;
-inline constexpr int EI_MAG1 = 1;
-inline constexpr int EI_MAG2 = 2;
-inline constexpr int EI_MAG3 = 3;
-inline constexpr int EI_CLASS = 4;
-inline constexpr int EI_DATA = 5;
-inline constexpr int EI_VERSION = 6;
-inline constexpr int EI_OSABI = 7;
-
-/* e_ident[] magic */
-inline constexpr uint8 ELFMAG0 = 0x7f;
-inline constexpr uint8 ELFMAG1 = 'E';
-inline constexpr uint8 ELFMAG2 = 'L';
-inline constexpr uint8 ELFMAG3 = 'F';
-
-/* e_ident[EI_CLASS] */
-inline constexpr uint8 ELFCLASS64 = 2;
-
-/* e_ident[EI_DATA] */
-inline constexpr uint8 ELFDATA2LSB = 1; /* Little-endian */
-
-/* e_type */
-inline constexpr Elf64_Half ET_EXEC = 2; /* Executable */
-inline constexpr Elf64_Half ET_DYN = 3; /* Shared object (PIE) */
-
-/* e_machine */
-inline constexpr Elf64_Half EM_RISCV = 243;
-
-/* ================================================================== */
-/* Program Header */
-/* ================================================================== */
-
-struct Elf64_Phdr {
- Elf64_Word p_type; /* Segment type */
- Elf64_Word p_flags; /* Segment flags */
- Elf64_Off p_offset; /* File offset */
- Elf64_Addr p_vaddr; /* Virtual address */
- Elf64_Addr p_paddr; /* Physical address (unused) */
- Elf64_Xword p_filesz; /* Size in file */
- Elf64_Xword p_memsz; /* Size in memory (>= p_filesz, diff is BSS) */
- Elf64_Xword p_align; /* Alignment */
-};
-
-/* p_type values */
-inline constexpr Elf64_Word PT_NULL = 0;
-inline constexpr Elf64_Word PT_LOAD = 1;
-inline constexpr Elf64_Word PT_DYNAMIC = 2;
-inline constexpr Elf64_Word PT_INTERP = 3;
-inline constexpr Elf64_Word PT_NOTE = 4;
-inline constexpr Elf64_Word PT_PHDR = 6;
-inline constexpr Elf64_Word PT_GNU_STACK = 0x6474e551;
-
-/* p_flags values */
-inline constexpr Elf64_Word PF_X = 0x1; /* Execute */
-inline constexpr Elf64_Word PF_W = 0x2; /* Write */
-inline constexpr Elf64_Word PF_R = 0x4; /* Read */
-
-/* ================================================================== */
-/* Auxiliary Vector types */
-/* ================================================================== */
-
-struct Elf64_auxv_t {
- uint64 a_type;
- uint64 a_val;
-};
-
-inline constexpr uint64 AT_NULL = 0;
-inline constexpr uint64 AT_PHDR = 3;
-inline constexpr uint64 AT_PHENT = 4;
-inline constexpr uint64 AT_PHNUM = 5;
-inline constexpr uint64 AT_PAGESZ = 6;
-inline constexpr uint64 AT_ENTRY = 9;
-inline constexpr uint64 AT_UID = 11;
-inline constexpr uint64 AT_EUID = 12;
-inline constexpr uint64 AT_GID = 13;
-inline constexpr uint64 AT_EGID = 14;
-inline constexpr uint64 AT_RANDOM = 25;
-inline constexpr uint64 AT_BASE = 7; /* PIE load base address */
-
-/* ================================================================== */
-/* RELA relocation entry — used by the kernel's own .rela.dyn walk */
-/* at boot (relocate_kernel in arch/riscv/mm/init.cc). */
-/* ================================================================== */
-
-struct Elf64_Rela {
- Elf64_Addr r_offset; /* link-time VA of the slot to patch */
- Elf64_Xword r_info; /* r_sym << 32 | r_type */
- Elf64_Sxword r_addend; /* link-time value of the target */
-};
-
-#define ELF64_R_TYPE(info) ((info) & 0xFFFFFFFFULL)
-
-/* RISC-V relocation types — only the ones a PIE-linked kernel emits
- * into .rela.dyn under `-shared -Bsymbolic`. */
-inline constexpr uint32 R_RISCV_NONE = 0;
-inline constexpr uint32 R_RISCV_64 = 2;
-inline constexpr uint32 R_RISCV_RELATIVE = 3;
diff --git a/na/include/lume/errno.h b/na/include/lume/errno.h
deleted file mode 100644
index f3bf09f..0000000
--- a/na/include/lume/errno.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _LUME_ERRNO_H
-#define _LUME_ERRNO_H
-
-/*
- * Error codes — numeric values aligned with Linux's asm-generic/errno-base.h,
- * asm-generic/errno.h, and linux/errno.h (kernel-internal codes 512+).
- *
- * Userspace-visible codes (1..133) match POSIX / Linux exactly so a LumeOS
- * binary returning -EINVAL to a user program receives the same number Linux
- * would. Kernel-internal codes (>= 512) follow Linux's convention of being
- * out of POSIX range; they are translated to a user-visible code (typically
- * EINTR) before crossing into user mode.
- *
- * Defined as plain #define so this header is usable from C, C++, and
- * assembly (the macros expand to integer literals, no types).
- */
-
-/* ===== Base errors (asm-generic/errno-base.h equivalent) ===== */
-#define EPERM 1 /* Operation not permitted */
-#define ENOENT 2 /* No such file or directory */
-#define ESRCH 3 /* No such process */
-#define EINTR 4 /* Interrupted system call */
-#define EIO 5 /* I/O error */
-#define ENXIO 6 /* No such device or address */
-#define E2BIG 7 /* Argument list too long */
-#define ENOEXEC 8 /* Exec format error */
-#define EBADF 9 /* Bad file number */
-#define ECHILD 10 /* No child processes */
-#define EAGAIN 11 /* Try again */
-#define ENOMEM 12 /* Out of memory */
-#define EACCES 13 /* Permission denied */
-#define EFAULT 14 /* Bad address */
-#define ENOTBLK 15 /* Block device required */
-#define EBUSY 16 /* Device or resource busy */
-#define EEXIST 17 /* File exists */
-#define EXDEV 18 /* Cross-device link */
-#define ENODEV 19 /* No such device */
-#define ENOTDIR 20 /* Not a directory */
-#define EISDIR 21 /* Is a directory */
-#define EINVAL 22 /* Invalid argument */
-#define ENFILE 23 /* File table overflow */
-#define EMFILE 24 /* Too many open files */
-#define ENOTTY 25 /* Not a typewriter */
-#define ETXTBSY 26 /* Text file busy */
-#define EFBIG 27 /* File too large */
-#define ENOSPC 28 /* No space left on device */
-#define ESPIPE 29 /* Illegal seek */
-#define EROFS 30 /* Read-only file system */
-#define EMLINK 31 /* Too many links */
-#define EPIPE 32 /* Broken pipe */
-#define EDOM 33 /* Math argument out of domain */
-#define ERANGE 34 /* Math result not representable */
-
-/* ===== Extended errors (asm-generic/errno.h equivalent) ===== */
-#define EDEADLK 35 /* Resource deadlock would occur */
-#define ENAMETOOLONG 36 /* File name too long */
-#define ENOLCK 37 /* No record locks available */
-#define ENOSYS 38 /* Invalid system call number */
-#define ENOTEMPTY 39 /* Directory not empty */
-#define ELOOP 40 /* Too many symbolic links encountered */
-#define EWOULDBLOCK EAGAIN /* Operation would block */
-#define ENOMSG 42 /* No message of desired type */
-#define EIDRM 43 /* Identifier removed */
-#define ECHRNG 44 /* Channel number out of range */
-#define EL2NSYNC 45 /* Level 2 not synchronized */
-#define EL3HLT 46 /* Level 3 halted */
-#define EL3RST 47 /* Level 3 reset */
-#define ELNRNG 48 /* Link number out of range */
-#define EUNATCH 49 /* Protocol driver not attached */
-#define ENOCSI 50 /* No CSI structure available */
-#define EL2HLT 51 /* Level 2 halted */
-#define EBADE 52 /* Invalid exchange */
-#define EBADR 53 /* Invalid request descriptor */
-#define EXFULL 54 /* Exchange full */
-#define ENOANO 55 /* No anode */
-#define EBADRQC 56 /* Invalid request code */
-#define EBADSLT 57 /* Invalid slot */
-#define EDEADLOCK EDEADLK
-#define EBFONT 59 /* Bad font file format */
-#define ENOSTR 60 /* Device not a stream */
-#define ENODATA 61 /* No data available */
-#define ETIME 62 /* Timer expired */
-#define ENOSR 63 /* Out of streams resources */
-#define ENONET 64 /* Machine is not on the network */
-#define ENOPKG 65 /* Package not installed */
-#define EREMOTE 66 /* Object is remote */
-#define ENOLINK 67 /* Link has been severed */
-#define EADV 68 /* Advertise error */
-#define ESRMNT 69 /* Srmount error */
-#define ECOMM 70 /* Communication error on send */
-#define EPROTO 71 /* Protocol error */
-#define EMULTIHOP 72 /* Multihop attempted */
-#define EDOTDOT 73 /* RFS specific error */
-#define EBADMSG 74 /* Not a data message */
-#define EOVERFLOW 75 /* Value too large for defined data type */
-#define ENOTUNIQ 76 /* Name not unique on network */
-#define EBADFD 77 /* File descriptor in bad state */
-#define EREMCHG 78 /* Remote address changed */
-#define ELIBACC 79 /* Cannot access a needed shared library */
-#define ELIBBAD 80 /* Accessing a corrupted shared library */
-#define ELIBSCN 81 /* .lib section in a.out corrupted */
-#define ELIBMAX 82 /* Linking in too many shared libraries */
-#define ELIBEXEC 83 /* Cannot exec a shared library directly */
-#define EILSEQ 84 /* Illegal byte sequence */
-#define ERESTART 85 /* Interrupted system call should be restarted */
-#define ESTRPIPE 86 /* Streams pipe error */
-#define EUSERS 87 /* Too many users */
-#define ENOTSOCK 88 /* Socket operation on non-socket */
-#define EDESTADDRREQ 89 /* Destination address required */
-#define EMSGSIZE 90 /* Message too long */
-#define EPROTOTYPE 91 /* Protocol wrong type for socket */
-#define ENOPROTOOPT 92 /* Protocol not available */
-#define EPROTONOSUPPORT 93 /* Protocol not supported */
-#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
-#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
-#define ENOTSUP EOPNOTSUPP
-#define EPFNOSUPPORT 96 /* Protocol family not supported */
-#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
-#define EADDRINUSE 98 /* Address already in use */
-#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
-#define ENETDOWN 100 /* Network is down */
-#define ENETUNREACH 101 /* Network is unreachable */
-#define ENETRESET 102 /* Network dropped connection because of reset */
-#define ECONNABORTED 103 /* Software caused connection abort */
-#define ECONNRESET 104 /* Connection reset by peer */
-#define ENOBUFS 105 /* No buffer space available */
-#define EISCONN 106 /* Transport endpoint is already connected */
-#define ENOTCONN 107 /* Transport endpoint is not connected */
-#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
-#define ETOOMANYREFS 109 /* Too many references: cannot splice */
-#define ETIMEDOUT 110 /* Connection timed out */
-#define ECONNREFUSED 111 /* Connection refused */
-#define EHOSTDOWN 112 /* Host is down */
-#define EHOSTUNREACH 113 /* No route to host */
-#define EALREADY 114 /* Operation already in progress */
-#define EINPROGRESS 115 /* Operation now in progress */
-#define ESTALE 116 /* Stale file handle */
-#define EUCLEAN 117 /* Structure needs cleaning */
-#define ENOTNAM 118 /* Not a XENIX named type file */
-#define ENAVAIL 119 /* No XENIX semaphores available */
-#define EISNAM 120 /* Is a named type file */
-#define EREMOTEIO 121 /* Remote I/O error */
-#define EDQUOT 122 /* Quota exceeded */
-#define ENOMEDIUM 123 /* No medium found */
-#define EMEDIUMTYPE 124 /* Wrong medium type */
-#define ECANCELED 125 /* Operation Canceled */
-#define ENOKEY 126 /* Required key not available */
-#define EKEYEXPIRED 127 /* Key has expired */
-#define EKEYREVOKED 128 /* Key has been revoked */
-#define EKEYREJECTED 129 /* Key was rejected by service */
-#define EOWNERDEAD 130 /* Owner died (for robust mutexes) */
-#define ENOTRECOVERABLE 131 /* State not recoverable */
-#define ERFKILL 132 /* Operation not possible due to RF-kill */
-#define EHWPOISON 133 /* Memory page has hardware error */
-
-/* ===== Kernel-internal errors (linux/errno.h equivalent, >= 512) =====
- * These should never be seen by user programs. signal/syscall layer
- * translates them to a user-visible code (typically EINTR) before
- * returning to userspace.
- */
-#define ERESTARTSYS 512
-#define ERESTARTNOINTR 513
-#define ERESTARTNOHAND 514 /* restart if no signal handler */
-#define ENOIOCTLCMD 515 /* No ioctl command */
-#define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */
-#define EPROBE_DEFER 517 /* Driver requests probe retry */
-#define EOPENSTALE 518 /* open() found a stale dentry */
-#define ENOPARAM 519 /* Parameter not supported */
-
-/* NFSv3 reserved range (521..531) */
-#define EBADHANDLE 521 /* Illegal NFS file handle */
-#define ENOTSYNC 522 /* Update synchronization mismatch */
-#define EBADCOOKIE 523 /* Cookie is stale */
-#define ENOTSUPP 524 /* Operation is not supported */
-#define ETOOSMALL 525 /* Buffer or request is too small */
-#define ESERVERFAULT 526 /* An untranslatable error occurred */
-#define EBADTYPE 527 /* Type not supported by server */
-#define EJUKEBOX 528 /* Request initiated, will not complete before timeout */
-#define EIOCBQUEUED 529 /* iocb queued, will get completion event */
-#define ERECALLCONFLICT 530 /* conflict with recalled state */
-#define ENOGRACE 531 /* NFS file lock reclaim refused */
-
-#endif /* _LUME_ERRNO_H */
diff --git a/na/include/lume/fdt.h b/na/include/lume/fdt.h
deleted file mode 100644
index 626cc48..0000000
--- a/na/include/lume/fdt.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * FdtManager — Flattened Device Tree parser
- *
- * Two-phase design:
- * Phase 1 (Early Scan): Static methods that walk the raw FDT blob
- * without any memory allocation. Used before PMM is available.
- * Phase 2 (Unflatten): Builds a persistent C++ DeviceNode object
- * tree once Slab is ready. The tree is then globally read-only.
- *
- * Concurrency: all init is done by BSP in the single-core window.
- * After unflattening the tree is immutable — no locks required.
- *
- * Reference: docs/specs/fdt.md
- */
-
-#include <lume/device_node.h>
-#include <lume/types.h>
-
-class FdtManager {
-public:
- // Construct with the physical address of the FDT blob.
- // Called via Placement New in fdt_init().
- FdtManager(uint64 fdt_paddr);
-
- // --- Phase 1: Early scan (static, allocation-free) ---
-
- // Extract physical memory base and size from the /memory node.
- // Panics if the FDT is invalid or /memory is missing.
- static void early_scan_mem(uint64 fdt_paddr, uint64 *base, uint64 *size);
-
- // Extract the UART base address and size by matching compatible = "ns16550a".
- // Falls back to platform default on failure.
- static void early_scan_uart(uint64 fdt_paddr, uint64 *uart_addr,
- uint64 *uart_size);
-
- static void early_scan_kaslr(uint64 fdt_paddr, uint64 *seed);
-
- // --- Phase 2: Object tree construction (requires Slab) ---
-
- // Walk the FDT blob and build the DeviceNode tree.
- void unflatten();
-
- // --- Query interface (read-only after unflatten) ---
-
- // Find the first node whose "compatible" property contains the string.
- // Returns nullptr if no match.
- DeviceNode *find_compatible(const char *compat_string);
-
- // Look up a node by its full path (e.g. "/soc/uart@10000000").
- // Returns nullptr if not found.
- DeviceNode *get_node_by_path(const char *path);
-
- // Return the stored FDT physical address.
- uint64 get_paddr() const { return fdt_paddr_; }
-
-private:
- uint64 fdt_paddr_; // Physical address of the FDT blob
- bool is_unflattened_ = false; // True after unflatten() completes
- DeviceNode *root_ = nullptr; // Root of the unflattened tree
-};
-
-/* Global FDT manager singleton (Placement New, see global_init.md §3.1) */
-extern FdtManager *g_fdt;
-
-// --- Public free-function API ---
-
-// Save FDT address and validate magic. Called early by BSP.
-void fdt_init(uint64 fdt_paddr);
-
-// Build the DeviceNode object tree. Requires Slab.
-void fdt_unflatten();
-
-// Driver probing helpers (delegate to g_fdt).
-DeviceNode *fdt_find_compatible(const char *compat_string);
-DeviceNode *fdt_get_node_by_path(const char *path);
-
-// Early scan wrappers (delegate to FdtManager static methods).
-void fdt_early_get_mem_info(uint64 *base, uint64 *size);
-void fdt_early_get_uart_info(uint64 *uart_addr, uint64 *uart_size);
-void fdt_early_get_kaslr_seed(uint64 *seed);
diff --git a/na/include/lume/fdt_prop.h b/na/include/lume/fdt_prop.h
deleted file mode 100644
index f8a263c..0000000
--- a/na/include/lume/fdt_prop.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * FDT Property
- *
- * Represents a single property within a DeviceNode (e.g. "reg",
- * "compatible"). The value pointer points directly into the
- * original FDT blob — no copies are made.
- *
- * Reference: docs/specs/fdt.md §3.1
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-
-struct FdtProperty {
- const char *name; // Property name (points into FDT strings block)
- const void *value; // Raw property data (points into FDT struct block)
- int len; // Length of value in bytes
- list_node link; // Intrusive link into DeviceNode's property list
-};
diff --git a/na/include/lume/fdtable.h b/na/include/lume/fdtable.h
deleted file mode 100644
index 6159519..0000000
--- a/na/include/lume/fdtable.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * fdtable.h — Per-task file descriptor table
- *
- * Modeled after Linux include/linux/fdtable.h.
- * Reference: docs/vfs/05_fdtable.md
- */
-
-#include <lume/types.h>
-#include <lume/atomic.h>
-#include <kernel/sync/spinlock.h>
-
-struct file;
-
-/* ================================================================== */
-/* files_struct — per-task open file table */
-/* ================================================================== */
-
-inline constexpr int NR_OPEN_DEFAULT = 256;
-
-struct files_struct {
- lume::atomic<int32> count; /* Reference count (CLONE_FILES) */
- Spinlock file_lock;
-
- int next_fd; /* Hint for next free fd */
- int max_fds; /* Current max (= NR_OPEN_DEFAULT) */
- file* fd_array[NR_OPEN_DEFAULT];
- uint64 close_on_exec_init[(NR_OPEN_DEFAULT + 63) / 64];
- uint64 open_fds_init[(NR_OPEN_DEFAULT + 63) / 64];
-};
-
-/* ================================================================== */
-/* API */
-/* ================================================================== */
-
-files_struct* alloc_files_struct();
-void put_files_struct(files_struct* files);
-files_struct* dup_fd(files_struct* old_files);
-
-int alloc_fd(files_struct* files, unsigned int start);
-void fd_install(files_struct* files, int fd, file* fp);
-file* fcheck(files_struct* files, int fd);
-int close_fd(files_struct* files, int fd);
-int do_dup2(files_struct* files, int oldfd, int newfd, int flags);
-void do_close_on_exec(files_struct* files);
diff --git a/na/include/lume/features.h b/na/include/lume/features.h
deleted file mode 100644
index 85cfb72..0000000
--- a/na/include/lume/features.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef _FEATURES_H
-#define _FEATURES_H
-
-#if defined(_ALL_SOURCE) && !defined(_GNU_SOURCE)
-#define _GNU_SOURCE 1
-#endif
-
-#if defined(_DEFAULT_SOURCE) && !defined(_BSD_SOURCE)
-#define _BSD_SOURCE 1
-#endif
-
-#if !defined(_POSIX_SOURCE) && !defined(_POSIX_C_SOURCE) \
- && !defined(_XOPEN_SOURCE) && !defined(_GNU_SOURCE) \
- && !defined(_BSD_SOURCE) && !defined(__STRICT_ANSI__)
-#define _BSD_SOURCE 1
-#define _XOPEN_SOURCE 700
-#endif
-
-#if __STDC_VERSION__ >= 199901L
-#define __restrict restrict
-#elif !defined(__GNUC__)
-#define __restrict
-#endif
-
-#if __STDC_VERSION__ >= 199901L || defined(__cplusplus)
-#define __inline inline
-#elif !defined(__GNUC__)
-#define __inline
-#endif
-
-#if __STDC_VERSION__ >= 201112L
-#elif defined(__GNUC__)
-#define _Noreturn __attribute__((__noreturn__))
-#else
-#define _Noreturn
-#endif
-
-#define __REDIR(x,y) __typeof__(x) x __asm__(#y)
-
-#endif
diff --git a/na/include/lume/file.h b/na/include/lume/file.h
deleted file mode 100644
index 349059b..0000000
--- a/na/include/lume/file.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * file.h — Open file description + file_operations + path + vfsmount
- *
- * Modeled after Linux include/linux/fs.h struct file / file_operations.
- * Reference: docs/vfs/04_file.md
- */
-
-#include <lume/types.h>
-#include <lume/atomic.h>
-#include <lume/fs_types.h>
-#include <kernel/sync/spinlock.h>
-
-struct inode;
-struct dentry;
-struct super_block;
-struct address_space;
-struct dir_context;
-struct VmAreaStruct;
-struct kstat;
-
-/* ================================================================== */
-/* vfsmount (public part) */
-/* ================================================================== */
-
-struct vfsmount {
- dentry* mnt_root;
- super_block* mnt_sb;
- int mnt_flags;
-};
-
-vfsmount* mntget(vfsmount* mnt);
-void mntput(vfsmount* mnt);
-
-/* ================================================================== */
-/* path */
-/* ================================================================== */
-
-struct path {
- vfsmount* mnt;
- dentry* de; /* Linux calls this 'dentry' but that conflicts in C++ */
-};
-
-void path_get(const path* p);
-void path_put(const path* p);
-
-/* ================================================================== */
-/* file */
-/* ================================================================== */
-
-struct file_operations;
-
-struct file {
- Spinlock f_lock;
- uint32 f_mode; /* FMODE_* */
- const file_operations* f_op;
- address_space* f_mapping;
- void* private_data;
- inode* f_inode;
- unsigned int f_flags; /* O_* */
-
- path f_path;
-
- int64 f_pos;
- lume::atomic<int32> f_count;
-};
-
-/* ================================================================== */
-/* Accessors */
-/* ================================================================== */
-
-inline inode* file_inode(const file* f) { return f->f_inode; }
-inline dentry* file_dentry(const file* f) { return f->f_path.de; }
-
-file* get_file(file* f);
-file* alloc_empty_file(int flags);
-void fput(file* fp);
-
-/* ================================================================== */
-/* dir_context (for iterate_shared / getdents64) */
-/* ================================================================== */
-
-struct dir_context;
-using filldir_t = bool (*)(dir_context* ctx, const char* name, int namelen,
- int64 offset, uint64 ino, unsigned int d_type);
-
-struct dir_context {
- filldir_t actor;
- int64 pos;
-};
-
-/* ================================================================== */
-/* file_operations */
-/* ================================================================== */
-
-struct file_operations {
- int64 (*llseek)(file* fp, int64 offset, int whence);
- int64 (*read)(file* fp, char* buf, uint64 count, int64* pos);
- int64 (*write)(file* fp, const char* buf, uint64 count, int64* pos);
- int (*iterate_shared)(file* fp, dir_context* ctx);
- uint32 (*poll)(file* fp, void* pt);
- long (*unlocked_ioctl)(file* fp, unsigned int cmd, uint64 arg);
- int (*mmap)(file* fp, VmAreaStruct* vma);
- int (*open)(inode* ip, file* fp);
- int (*flush)(file* fp);
- int (*release)(inode* ip, file* fp);
- int (*fsync)(file* fp, int64 start, int64 end, int datasync);
-};
-
-/* ================================================================== */
-/* VFS read/write/seek */
-/* ================================================================== */
-
-int64 vfs_read(file* fp, char* buf, uint64 count, int64* pos);
-int64 vfs_write(file* fp, const char* buf, uint64 count, int64* pos);
-int64 vfs_llseek(file* fp, int64 offset, int whence);
-int vfs_fstat(file* fp, kstat* stat);
-
-/* Generic helpers for simple fs */
-int64 generic_file_llseek(file* fp, int64 offset, int whence);
-
-/* ================================================================== */
-/* path helpers */
-/* ================================================================== */
-
-inline bool path_equal(const path* a, const path* b) {
- return a->mnt == b->mnt && a->de == b->de;
-}
diff --git a/na/include/lume/fs.h b/na/include/lume/fs.h
deleted file mode 100644
index 8dac552..0000000
--- a/na/include/lume/fs.h
+++ /dev/null
@@ -1,310 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * fs.h — Core VFS data structures
- *
- * Defines super_block, inode, address_space, and their operations.
- * Modeled after Linux include/linux/fs.h + include/linux/fs/super_types.h.
- *
- * Reference: docs/vfs/01_super_block.md, docs/vfs/02_inode.md
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <lume/atomic.h>
-#include <lume/fs_types.h>
-#include <kernel/sync/spinlock.h>
-
-/* Forward declarations */
-struct super_block;
-struct inode;
-struct dentry;
-struct file;
-struct vfsmount;
-struct path;
-struct kstat;
-struct iattr;
-struct file_operations;
-struct inode_operations;
-struct dentry_operations;
-struct dir_context;
-struct pipe_inode_info;
-struct VmAreaStruct;
-struct Page;
-
-/* ================================================================== */
-/* address_space_operations */
-/* ================================================================== */
-
-struct address_space_operations {
- int (*read_folio)(file* fp, Page* page);
- int (*write_begin)(struct address_space* mapping, int64 pos,
- unsigned len, Page** pagep, void** fsdata);
- int (*write_end)(struct address_space* mapping, int64 pos,
- unsigned len, unsigned copied,
- Page* page, void* fsdata);
-};
-
-/* ================================================================== */
-/* address_space */
-/* ================================================================== */
-
-struct address_space {
- inode* host;
- const address_space_operations* a_ops;
- uint64 nrpages;
- uint64 flags;
- Spinlock i_private_lock;
-};
-
-/* ================================================================== */
-/* super_operations */
-/* ================================================================== */
-
-struct super_operations {
- inode* (*alloc_inode)(super_block* sb);
- void (*destroy_inode)(inode* ip);
- void (*free_inode)(inode* ip);
-
- void (*dirty_inode)(inode* ip, int flags);
- int (*write_inode)(inode* ip, bool sync);
- int (*drop_inode)(inode* ip);
- void (*evict_inode)(inode* ip);
- void (*put_super)(super_block* sb);
- int (*sync_fs)(super_block* sb, int wait);
- int (*statfs)(dentry* de, struct kstatfs* buf);
- void (*umount_begin)(super_block* sb);
-};
-
-/* ================================================================== */
-/* file_system_type */
-/* ================================================================== */
-
-struct file_system_type {
- const char* name;
- int fs_flags;
- super_block* (*mount)(file_system_type* type, int flags,
- const char* dev_name, void* data);
- void (*kill_sb)(super_block* sb);
- list_node fs_list;
-};
-
-int register_filesystem(file_system_type* fs);
-file_system_type* get_fs_type(const char* name);
-
-/* ================================================================== */
-/* super_block */
-/* ================================================================== */
-
-struct mount;
-
-struct super_block {
- list_node s_list;
- uint64 s_dev;
- uint8 s_blocksize_bits;
- uint64 s_blocksize;
- int64 s_maxbytes;
- file_system_type* s_type;
- const super_operations* s_op;
-
- uint64 s_flags;
- uint64 s_magic;
- dentry* s_root;
-
- char s_id[32];
- void* s_fs_info;
-
- uint32 s_time_gran;
- int64 s_time_min;
- int64 s_time_max;
-
- Spinlock s_inode_list_lock;
- list_head s_inodes;
-
- lume::atomic<int32> s_active;
- int s_count;
-
- mount* s_mounts;
- uint32 s_max_links;
- const dentry_operations* s_d_op;
-};
-
-/* ================================================================== */
-/* inode */
-/* ================================================================== */
-
-struct inode {
- /* ---- Path-walk hot fields ---- */
- uint32 i_mode;
- uint32 i_flags;
- uint32 i_uid;
- uint32 i_gid;
-
- const inode_operations* i_op;
- super_block* i_sb;
- address_space* i_mapping;
-
- /* ---- Stat data ---- */
- uint64 i_ino;
- uint32 i_nlink; /* Use set_nlink/inc_nlink/drop_nlink */
- uint32 i_rdev;
- int64 i_size;
-
- int64 i_atime_sec;
- int64 i_mtime_sec;
- int64 i_ctime_sec;
- uint32 i_atime_nsec;
- uint32 i_mtime_nsec;
- uint32 i_ctime_nsec;
- uint32 i_generation;
-
- Spinlock i_lock;
- unsigned short i_bytes;
- uint8 i_blkbits;
- int64 i_blocks;
-
- /* ---- State ---- */
- uint32 i_state;
-
- /* ---- Lists ---- */
- list_node i_sb_list;
- list_head i_dentry;
-
- /* ---- Counters ---- */
- lume::atomic<int32> i_count;
- lume::atomic<int32> i_writecount;
-
- /* ---- File ops ---- */
- const file_operations* i_fop;
-
- /* ---- Embedded address_space ---- */
- address_space i_data;
-
- /* ---- Type-specific ---- */
- union {
- pipe_inode_info* i_pipe;
- char* i_link;
- uint32 i_dir_seq;
- };
-
- void* i_private;
-};
-
-/* ================================================================== */
-/* inode_operations */
-/* ================================================================== */
-
-struct inode_operations {
- dentry* (*lookup)(inode* dir, dentry* de, unsigned int flags);
- int (*create)(inode* dir, dentry* de, uint32 mode, bool excl);
- int (*link)(dentry* old_de, inode* dir, dentry* new_de);
- int (*unlink)(inode* dir, dentry* de);
- int (*symlink)(inode* dir, dentry* de, const char* target);
- int (*mkdir)(inode* dir, dentry* de, uint32 mode);
- int (*rmdir)(inode* dir, dentry* de);
- int (*mknod)(inode* dir, dentry* de, uint32 mode, uint32 rdev);
- int (*rename)(inode* old_dir, dentry* old_de,
- inode* new_dir, dentry* new_de, unsigned int flags);
- int (*setattr)(dentry* de, iattr* attr);
- int (*getattr)(const path* p, kstat* stat, uint32 request_mask,
- unsigned int query_flags);
- const char* (*get_link)(dentry* de, inode* ip);
- int (*permission)(inode* ip, int mask);
-};
-
-/* ================================================================== */
-/* Inode API */
-/* ================================================================== */
-
-inode* new_inode(super_block* sb);
-inode* iget(super_block* sb, uint64 ino);
-void iput(inode* ip);
-void ihold(inode* ip);
-void mark_inode_dirty(inode* ip);
-
-void set_nlink(inode* ip, uint32 nlink);
-void inc_nlink(inode* ip);
-void drop_nlink(inode* ip);
-void clear_nlink(inode* ip);
-
-void inode_init_owner(inode* ip, const inode* dir, uint32 mode);
-
-/* Generic helpers used by simple filesystems */
-void generic_fillattr(inode* ip, kstat* stat);
-
-/* ================================================================== */
-/* kstat (matches oscomp ABI layout exactly) */
-/* ================================================================== */
-
-struct kstat {
- uint64 st_dev;
- uint64 st_ino;
- uint32 st_mode;
- uint32 st_nlink;
- uint32 st_uid;
- uint32 st_gid;
- uint64 st_rdev;
- uint64 __pad;
- int64 st_size;
- int32 st_blksize;
- int32 __pad2;
- int64 st_blocks;
- int64 st_atime_sec;
- int64 st_atime_nsec;
- int64 st_mtime_sec;
- int64 st_mtime_nsec;
- int64 st_ctime_sec;
- int64 st_ctime_nsec;
- uint32 __unused[2];
-};
-
-struct kstatfs {
- int64 f_type;
- int64 f_bsize;
- uint64 f_blocks;
- uint64 f_bfree;
- uint64 f_bavail;
- uint64 f_files;
- uint64 f_ffree;
- int32 f_fsid[2];
- int64 f_namelen;
- int64 f_frsize;
- int64 f_flags;
- int64 f_spare[4];
-};
-
-/* ================================================================== */
-/* iattr (for setattr) */
-/* ================================================================== */
-
-struct iattr {
- unsigned int ia_valid; /* ATTR_* flags */
- uint32 ia_mode;
- uint32 ia_uid;
- uint32 ia_gid;
- int64 ia_size;
- int64 ia_atime_sec;
- int64 ia_atime_nsec;
- int64 ia_mtime_sec;
- int64 ia_mtime_nsec;
- int64 ia_ctime_sec;
- int64 ia_ctime_nsec;
-};
-
-/* ATTR_* flags */
-inline constexpr uint32 ATTR_MODE = (1U << 0);
-inline constexpr uint32 ATTR_UID = (1U << 1);
-inline constexpr uint32 ATTR_GID = (1U << 2);
-inline constexpr uint32 ATTR_SIZE = (1U << 3);
-inline constexpr uint32 ATTR_ATIME = (1U << 4);
-inline constexpr uint32 ATTR_MTIME = (1U << 5);
-inline constexpr uint32 ATTR_CTIME = (1U << 6);
-
-/* ================================================================== */
-/* VFS init */
-/* ================================================================== */
-
-void vfs_init();
diff --git a/na/include/lume/fs_struct.h b/na/include/lume/fs_struct.h
deleted file mode 100644
index ba643b5..0000000
--- a/na/include/lume/fs_struct.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * fs_struct.h — Per-task filesystem context (cwd, root)
- *
- * Modeled after Linux include/linux/fs_struct.h.
- * Reference: docs/vfs/05_fdtable.md
- */
-
-#include <lume/types.h>
-#include <lume/atomic.h>
-#include <lume/file.h> /* struct path */
-#include <kernel/sync/spinlock.h>
-
-/* ================================================================== */
-/* fs_struct */
-/* ================================================================== */
-
-struct fs_struct {
- lume::atomic<int32> count; /* Reference count (CLONE_FS) */
- Spinlock lock;
- int umask; /* File creation mask */
- path root; /* Root directory */
- path pwd; /* Current working directory */
-};
-
-/* ================================================================== */
-/* API */
-/* ================================================================== */
-
-fs_struct* alloc_fs_struct();
-void put_fs_struct(fs_struct* fs);
-fs_struct* copy_fs_struct(const fs_struct* old_fs);
-
-void set_fs_root(fs_struct* fs, const path* p);
-void set_fs_pwd(fs_struct* fs, const path* p);
diff --git a/na/include/lume/fs_types.h b/na/include/lume/fs_types.h
deleted file mode 100644
index 767f37c..0000000
--- a/na/include/lume/fs_types.h
+++ /dev/null
@@ -1,197 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * fs_types.h — File mode constants, open flags, seek constants
- *
- * All values match Linux asm-generic so musl/busybox binaries work.
- * Reference: docs/vfs/02_inode.md, docs/vfs/04_file.md
- */
-
-#include <lume/types.h>
-
-/* ================================================================== */
-/* S_IF* — file type in inode::i_mode upper bits */
-/* ================================================================== */
-
-inline constexpr uint32 S_IFMT = 0170000;
-inline constexpr uint32 S_IFSOCK = 0140000;
-inline constexpr uint32 S_IFLNK = 0120000;
-inline constexpr uint32 S_IFREG = 0100000;
-inline constexpr uint32 S_IFBLK = 0060000;
-inline constexpr uint32 S_IFDIR = 0040000;
-inline constexpr uint32 S_IFCHR = 0020000;
-inline constexpr uint32 S_IFIFO = 0010000;
-
-inline constexpr uint32 S_ISUID = 04000;
-inline constexpr uint32 S_ISGID = 02000;
-inline constexpr uint32 S_ISVTX = 01000;
-
-inline constexpr uint32 S_IRWXU = 00700;
-inline constexpr uint32 S_IRUSR = 00400;
-inline constexpr uint32 S_IWUSR = 00200;
-inline constexpr uint32 S_IXUSR = 00100;
-inline constexpr uint32 S_IRWXG = 00070;
-inline constexpr uint32 S_IRGRP = 00040;
-inline constexpr uint32 S_IWGRP = 00020;
-inline constexpr uint32 S_IXGRP = 00010;
-inline constexpr uint32 S_IRWXO = 00007;
-inline constexpr uint32 S_IROTH = 00004;
-inline constexpr uint32 S_IWOTH = 00002;
-inline constexpr uint32 S_IXOTH = 00001;
-
-inline bool S_ISREG(uint32 m) { return (m & S_IFMT) == S_IFREG; }
-inline bool S_ISDIR(uint32 m) { return (m & S_IFMT) == S_IFDIR; }
-inline bool S_ISCHR(uint32 m) { return (m & S_IFMT) == S_IFCHR; }
-inline bool S_ISBLK(uint32 m) { return (m & S_IFMT) == S_IFBLK; }
-inline bool S_ISFIFO(uint32 m) { return (m & S_IFMT) == S_IFIFO; }
-inline bool S_ISLNK(uint32 m) { return (m & S_IFMT) == S_IFLNK; }
-inline bool S_ISSOCK(uint32 m) { return (m & S_IFMT) == S_IFSOCK; }
-
-/* ================================================================== */
-/* O_* — open(2) flags */
-/* ================================================================== */
-
-inline constexpr int O_RDONLY = 00;
-inline constexpr int O_WRONLY = 01;
-inline constexpr int O_RDWR = 02;
-inline constexpr int O_ACCMODE = 03;
-inline constexpr int O_CREAT = 0100;
-inline constexpr int O_EXCL = 0200;
-inline constexpr int O_NOCTTY = 0400;
-inline constexpr int O_TRUNC = 01000;
-inline constexpr int O_APPEND = 02000;
-inline constexpr int O_NONBLOCK = 04000;
-inline constexpr int O_DSYNC = 010000;
-inline constexpr int O_DIRECTORY = 0200000;
-inline constexpr int O_NOFOLLOW = 0400000;
-inline constexpr int O_CLOEXEC = 02000000;
-
-/* ================================================================== */
-/* AT_* — *at() syscall dirfd constants */
-/* ================================================================== */
-
-inline constexpr int AT_FDCWD = -100;
-inline constexpr int AT_REMOVEDIR = 0x200;
-inline constexpr int AT_SYMLINK_NOFOLLOW = 0x100;
-inline constexpr int AT_EMPTY_PATH = 0x1000;
-
-/* ================================================================== */
-/* SEEK_* — lseek(2) whence */
-/* ================================================================== */
-
-inline constexpr int SEEK_SET = 0;
-inline constexpr int SEEK_CUR = 1;
-inline constexpr int SEEK_END = 2;
-
-/* ================================================================== */
-/* FMODE_* — internal file mode flags */
-/* ================================================================== */
-
-inline constexpr uint32 FMODE_READ = (1U << 0);
-inline constexpr uint32 FMODE_WRITE = (1U << 1);
-inline constexpr uint32 FMODE_LSEEK = (1U << 2);
-inline constexpr uint32 FMODE_PREAD = (1U << 3);
-inline constexpr uint32 FMODE_PWRITE = (1U << 4);
-inline constexpr uint32 FMODE_EXEC = (1U << 5);
-inline constexpr uint32 FMODE_CAN_READ = (1U << 17);
-inline constexpr uint32 FMODE_CAN_WRITE = (1U << 18);
-inline constexpr uint32 FMODE_OPENED = (1U << 19);
-inline constexpr uint32 FMODE_CREATED = (1U << 20);
-inline constexpr uint32 FMODE_STREAM = (1U << 21);
-
-/* ================================================================== */
-/* MAY_* — permission check flags */
-/* ================================================================== */
-
-inline constexpr int MAY_EXEC = 0x00000001;
-inline constexpr int MAY_WRITE = 0x00000002;
-inline constexpr int MAY_READ = 0x00000004;
-inline constexpr int MAY_OPEN = 0x00000020;
-inline constexpr int MAY_CHDIR = 0x00000040;
-
-/* ================================================================== */
-/* DT_* — directory entry types (for getdents64) */
-/* ================================================================== */
-
-inline constexpr uint8 DT_UNKNOWN = 0;
-inline constexpr uint8 DT_FIFO = 1;
-inline constexpr uint8 DT_CHR = 2;
-inline constexpr uint8 DT_DIR = 4;
-inline constexpr uint8 DT_BLK = 6;
-inline constexpr uint8 DT_REG = 8;
-inline constexpr uint8 DT_LNK = 10;
-inline constexpr uint8 DT_SOCK = 12;
-
-/* ================================================================== */
-/* SB_* — super_block flags */
-/* ================================================================== */
-
-inline constexpr uint64 SB_RDONLY = (1ULL << 0);
-inline constexpr uint64 SB_NOSUID = (1ULL << 1);
-inline constexpr uint64 SB_NODEV = (1ULL << 2);
-inline constexpr uint64 SB_NOEXEC = (1ULL << 3);
-inline constexpr uint64 SB_SYNCHRONOUS = (1ULL << 4);
-inline constexpr uint64 SB_NOATIME = (1ULL << 10);
-inline constexpr uint64 SB_NODIRATIME = (1ULL << 11);
-inline constexpr uint64 SB_SILENT = (1ULL << 15);
-inline constexpr uint64 SB_KERNMOUNT = (1ULL << 22);
-inline constexpr uint64 SB_LAZYTIME = (1ULL << 25);
-inline constexpr uint64 SB_ACTIVE = (1ULL << 30);
-
-/* ================================================================== */
-/* MNT_* — mount flags */
-/* ================================================================== */
-
-inline constexpr int MNT_NOSUID = 0x01;
-inline constexpr int MNT_NODEV = 0x02;
-inline constexpr int MNT_NOEXEC = 0x04;
-inline constexpr int MNT_NOATIME = 0x08;
-inline constexpr int MNT_RELATIME = 0x20;
-inline constexpr int MNT_READONLY = 0x40;
-inline constexpr int MNT_INTERNAL = 0x4000;
-
-/* ================================================================== */
-/* Inode state flags */
-/* ================================================================== */
-
-inline constexpr uint32 I_NEW = (1U << 0);
-inline constexpr uint32 I_DIRTY_SYNC = (1U << 4);
-inline constexpr uint32 I_DIRTY_DATASYNC = (1U << 5);
-inline constexpr uint32 I_DIRTY_PAGES = (1U << 6);
-inline constexpr uint32 I_WILL_FREE = (1U << 7);
-inline constexpr uint32 I_FREEING = (1U << 8);
-inline constexpr uint32 I_CLEAR = (1U << 9);
-inline constexpr uint32 I_REFERENCED = (1U << 10);
-inline constexpr uint32 I_DIRTY_TIME = (1U << 12);
-inline constexpr uint32 I_CREATING = (1U << 15);
-
-inline constexpr uint32 I_DIRTY_INODE = I_DIRTY_SYNC | I_DIRTY_DATASYNC;
-inline constexpr uint32 I_DIRTY = I_DIRTY_INODE | I_DIRTY_PAGES;
-
-/* ================================================================== */
-/* DCACHE_* — dentry flags */
-/* ================================================================== */
-
-inline constexpr uint32 DCACHE_OP_HASH = (1U << 0);
-inline constexpr uint32 DCACHE_OP_COMPARE = (1U << 1);
-inline constexpr uint32 DCACHE_OP_REVALIDATE = (1U << 2);
-inline constexpr uint32 DCACHE_DISCONNECTED = (1U << 5);
-inline constexpr uint32 DCACHE_REFERENCED = (1U << 6);
-inline constexpr uint32 DCACHE_CANT_MOUNT = (1U << 8);
-inline constexpr uint32 DCACHE_MOUNTED = (1U << 15);
-inline constexpr uint32 DCACHE_ENTRY_TYPE = (7U << 19);
-inline constexpr uint32 DCACHE_MISS_TYPE = (0U << 19);
-inline constexpr uint32 DCACHE_DIRECTORY_TYPE = (2U << 19);
-inline constexpr uint32 DCACHE_REGULAR_TYPE = (4U << 19);
-inline constexpr uint32 DCACHE_SPECIAL_TYPE = (5U << 19);
-inline constexpr uint32 DCACHE_SYMLINK_TYPE = (6U << 19);
-
-/* ================================================================== */
-/* FS_* — file_system_type flags */
-/* ================================================================== */
-
-inline constexpr int FS_REQUIRES_DEV = 1;
-inline constexpr int FS_NO_DEVICE = 2;
diff --git a/na/include/lume/init.h b/na/include/lume/init.h
deleted file mode 100644
index 541ab96..0000000
--- a/na/include/lume/init.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _LUME_INIT_H
-#define _LUME_INIT_H
-
-/*
- * Boot-time section markers.
- *
- * Mirrors the subset of Linux include/linux/init.h that LumeOS uses:
- *
- * __init mark a function as boot-only — emitted into .init.text,
- * which the kernel can later free once setup_arch /
- * start_kernel have finished (LumeOS doesn't yet reclaim
- * this memory, but the segment lives so future versions
- * can without source changes).
- * __initdata same for data, emitted into .init.data.
- * asmlinkage ABI marker for C functions called from assembly. On
- * RISC-V the default C ABI matches the assembly calling
- * convention, so this is empty (matches Linux RISC-V).
- *
- * No attempt is made to mirror Linux's COMMAND_LINE_SIZE / saved_command_line
- * machinery — that belongs in a future <lume/setup.h>.
- */
-
-#define __init __attribute__((section(".init.text")))
-#define __initdata __attribute__((section(".init.data")))
-
-#ifndef asmlinkage
-#define asmlinkage /* RISC-V: empty, ABI already matches */
-#endif
-
-#endif /* _LUME_INIT_H */
diff --git a/na/include/lume/irq.h b/na/include/lume/irq.h
deleted file mode 100644
index f5f34e4..0000000
--- a/na/include/lume/irq.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * IRQ Manager — Dynamic Interrupt Dispatch
- *
- * Drivers register ISR callbacks at init time via register_handler().
- * The MI trap layer calls dispatch() when an external interrupt arrives.
- * dispatch() runs in interrupt context — handlers must be short and
- * non-blocking.
- *
- * Lock contract:
- * - lock_ protects register/unregister (cold path).
- * - dispatch() is lock-free: handler pointers are published with RELEASE
- * semantics and read with ACQUIRE in dispatch().
- * - Handlers must not acquire VmSpace::lock or sleep.
- *
- * Reference: docs/specs/trap.md §3.3
- */
-
-#include <lume/atomic.h>
-#include <lume/types.h>
-
-/* IRQ handler callback signature */
-using IrqHandler = void (*)(uint32 irq, void *arg);
-
-/* Abstract Interrupt Controller Interface */
-struct IrqChip {
- const char* name;
- uint32 (*claim)();
- void (*complete)(uint32 irq);
- void (*init_ap)();
- void (*enable)(uint32 irq);
- void (*disable)(uint32 irq);
- void (*set_priority)(uint32 irq, uint32 priority);
-};
-
-class IrqManager {
-public:
- void init();
-
- /* Register an ISR for the given IRQ number.
- * Returns 0 on success, -EINVAL if irq out of range, -EBUSY if already
- * registered. */
- int register_handler(uint32 irq, IrqHandler handler, void *arg);
-
- /* Unregister the ISR for the given IRQ number. */
- void unregister_handler(uint32 irq);
-
- /* Dispatch an interrupt to the registered handler.
- * Called from MI trap layer in interrupt context. Lock-free. */
- void dispatch(uint32 irq);
-
- /* Set the active interrupt controller chip */
- void set_chip(const IrqChip* chip);
- const IrqChip* get_chip() const { return chip_; }
-
- /* Architecture-independent claim and complete for the active IRQ chip */
- uint32 claim() const;
- void complete(uint32 irq) const;
- void init_ap() const;
- void enable(uint32 irq) const;
- void disable(uint32 irq) const;
- void set_priority(uint32 irq, uint32 priority) const;
-
- /* Check if a handler is registered for the given IRQ. */
- bool has_handler(uint32 irq) const;
-
- static constexpr uint32 kMaxIrqs = 64;
-
-private:
- struct IrqEntry {
- lume::atomic<uint64> handler_ptr; // Stored as uint64 for atomic access
- void *arg = nullptr;
- };
-
- const IrqChip* chip_ = nullptr;
- IrqEntry entries_[kMaxIrqs];
-};
-
-/* Global IRQ manager singleton */
-extern IrqManager g_irq_manager;
diff --git a/na/include/lume/kbuild.h b/na/include/lume/kbuild.h
deleted file mode 100644
index 4c3e12a..0000000
--- a/na/include/lume/kbuild.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _LUME_KBUILD_H
-#define _LUME_KBUILD_H
-
-/*
- * Helpers for arch/<arch>/kernel/asm-offsets.c — 1:1 with Linux
- * include/linux/kbuild.h.
- *
- * Trick: each DEFINE/OFFSET emits an inline-asm ".ascii" directive
- * containing a literal "->" prefix. When the file is compiled with
- *
- * $(CC) -S asm-offsets.c -o asm-offsets.s
- *
- * the resulting assembly contains lines like
- *
- * ->KERNEL_MAP_VIRT_ADDR $0 offsetof(struct kernel_mapping, virt_addr)
- *
- * scripts/extract-asm-offsets.sed then rewrites those into proper
- * `#define KERNEL_MAP_VIRT_ADDR 0` lines for inclusion from head.S.
- *
- * The benefit over hand-written #defines is that struct layout changes
- * are caught at build time: rearrange a field and the asm offset
- * changes automatically.
- */
-
-#define DEFINE(sym, val) \
- asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val))
-
-#define BLANK() \
- asm volatile("\n.ascii \"->\"" : : )
-
-#define OFFSET(sym, str, mem) \
- DEFINE(sym, offsetof(struct str, mem))
-
-#define COMMENT(x) \
- asm volatile("\n.ascii \"->#" x "\"")
-
-#endif /* _LUME_KBUILD_H */
diff --git a/na/include/lume/kernel_map.h b/na/include/lume/kernel_map.h
deleted file mode 100644
index 7eafe88..0000000
--- a/na/include/lume/kernel_map.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _LUME_KERNEL_MAP_H
-#define _LUME_KERNEL_MAP_H
-
-/*
- * struct kernel_mapping — where the kernel image lives in physical and
- * virtual memory. Field names and order match Linux's
- * arch/riscv/include/asm/page.h struct kernel_mapping so that:
- *
- * 1. arch/riscv/kernel/head.S can do `la a1, kernel_map` then
- * `REG_L a1, KERNEL_MAP_VIRT_ADDR(a1)` and read the virtual
- * address. The KERNEL_MAP_* asm constants come from
- * <generated/asm-offsets.h>, produced by arch/riscv/kernel/
- * asm-offsets.c at build time — re-order the struct and the
- * asm offsets follow automatically.
- *
- * 2. arch/riscv/mm/init.c (Linux origin) populates the same fields
- * with the same semantics so no source edits are needed.
- */
-
-#ifndef __ASSEMBLY__
-
-#include <lume/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct kernel_mapping {
- unsigned long virt_addr;
- unsigned long virt_offset;
- uintptr_t phys_addr;
- uintptr_t size;
- unsigned long va_pa_offset;
- unsigned long page_offset;
- unsigned long va_kernel_pa_offset;
-};
-
-extern struct kernel_mapping kernel_map;
-
-#ifdef __cplusplus
-}
-#endif
-
-#else /* __ASSEMBLY__ */
-
-/* Assembly sees only the field offsets, picked up from the generated
- * header. Including the C struct above would be a parse error. */
-#include <generated/asm-offsets.h>
-
-#endif /* __ASSEMBLY__ */
-
-#endif /* _LUME_KERNEL_MAP_H */
diff --git a/na/include/lume/klog.h b/na/include/lume/klog.h
deleted file mode 100644
index 69ab269..0000000
--- a/na/include/lume/klog.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Kernel Logging Subsystem
- *
- * All kernel output goes through klog(), which formats messages into
- * a fixed-size ring buffer and optionally emits them to the console.
- *
- * Architecture:
- * vsnprintf_() ← pure formatting (mpaland/printf)
- * ↓
- * KlogBuffer (ring) ← all messages stored here
- * ↓
- * early_putc() (sync) ← current: synchronous UART output
- * klogd thread (async) ← future: background flush thread
- * /proc/kmsg ← future: userspace dmesg interface
- *
- * Reference: docs/specs/klog.md
- */
-
-#include <lume/types.h>
-
-/* ========================================================================
- * Log Levels (matches Linux kern_levels.h ordering)
- * ======================================================================== */
-
-enum KlogLevel : uint8 {
- KLOG_EMERG = 0, /* System is unusable */
- KLOG_ALERT = 1, /* Action must be taken immediately */
- KLOG_CRIT = 2, /* Critical conditions */
- KLOG_ERR = 3, /* Error conditions */
- KLOG_WARN = 4, /* Warning conditions */
- KLOG_NOTICE = 5, /* Normal but significant condition */
- KLOG_INFO = 6, /* Informational */
- KLOG_DEBUG = 7, /* Debug-level messages */
-};
-
-/* Log subsystems (bitmask-friendly) */
-enum KlogSubsys : uint8 {
- KLOG_SUBSYS_GENERIC = 0,
- KLOG_SUBSYS_SCHED = 1,
- KLOG_SUBSYS_TRAP = 2,
- KLOG_SUBSYS_IRQ = 3,
- KLOG_SUBSYS_TIMER = 4,
- KLOG_SUBSYS_VMM = 5,
- KLOG_SUBSYS_PMM = 6,
- KLOG_SUBSYS_SLAB = 7,
- KLOG_SUBSYS_FS = 8,
- KLOG_SUBSYS_DRIVER = 9,
- KLOG_SUBSYS_TEST = 10,
- KLOG_SUBSYS_SYSCALL = 11, /* per-syscall entry trace */
- KLOG_SUBSYS_EXEC = 12, /* ELF load / exec internals */
- KLOG_SUBSYS_SIGNAL = 13, /* signal delivery trace */
- KLOG_SUBSYS_TTY = 14, /* tty / job-control trace */
- KLOG_SUBSYS_MAX = 15,
-};
-
-/* Default: show everything up to INFO. DEBUG is compile-gated. */
-#ifndef KLOG_DEFAULT_LEVEL
-#define KLOG_DEFAULT_LEVEL KLOG_INFO
-#endif
-
-/* Ring buffer size: 2^KLOG_BUF_SHIFT bytes (default 64KB) */
-#ifndef KLOG_BUF_SHIFT
-#define KLOG_BUF_SHIFT 16
-#endif
-
-#define KLOG_BUF_SIZE (1U << KLOG_BUF_SHIFT)
-
-/* Maximum length of a single log message (stack-allocated) */
-#define KLOG_LINE_MAX 256
-
-struct KlogStats {
- uint32 head;
- uint32 tail;
- uint32 console_tail;
- uint32 dropped;
- uint32 seq;
-};
-
-struct KlogRateLimit {
- uint64 last_ts;
- uint32 interval;
- uint32 burst;
- uint32 printed;
- uint32 suppressed;
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* ========================================================================
- * Initialization
- * ======================================================================== */
-
-/* Initialize the ring buffer and console backend. Call once from BSP. */
-void klog_init(void);
-
-/* ========================================================================
- * Core API
- * ======================================================================== */
-
-/* Write a leveled log message. Formats into ring buffer, then
- * synchronously flushes to console if level <= current threshold. */
-__attribute__((format(printf, 2, 3)))
-void klog(KlogLevel level, const char* fmt, ...);
-
-__attribute__((format(printf, 3, 4)))
-void klog_ex(KlogLevel level, KlogSubsys subsys, const char* fmt, ...);
-
-/* Linux-style printk interface */
-__attribute__((format(printf, 1, 2)))
-int printk(const char* fmt, ...);
-int vprintk(const char* fmt, __builtin_va_list va);
-
-/* Set runtime log level filter. Messages above this level are
- * still stored in the ring buffer but not printed to console. */
-void klog_set_level(KlogLevel level);
-
-void klog_set_subsys_mask(uint64 mask);
-void klog_enable_subsys(KlogSubsys subsys);
-void klog_disable_subsys(KlogSubsys subsys);
-
-/* ========================================================================
- * Ring Buffer Read (for future dmesg / /proc/kmsg)
- * ======================================================================== */
-
-/* Read up to 'size' bytes from the ring buffer into 'buf'.
- * Returns number of bytes read. Non-destructive peek. */
-int klog_read(char* buf, int size);
-
-/* Read and consume entries (advances tail). */
-int klog_read_consume(char* buf, int size);
-
-/* Flush pending entries to console (advances console tail). */
-void klog_flush(void);
-
-bool klog_ratelimit(KlogRateLimit* rl, uint64 now);
-
-void klog_stats(KlogStats* out);
-
-/* ========================================================================
- * Convenience Macros
- * ======================================================================== */
-
-#define pr_emerg(fmt, ...) klog_ex(KLOG_EMERG, KLOG_SUBSYS_GENERIC, fmt, ##__VA_ARGS__)
-#define pr_alert(fmt, ...) klog_ex(KLOG_ALERT, KLOG_SUBSYS_GENERIC, fmt, ##__VA_ARGS__)
-#define pr_crit(fmt, ...) klog_ex(KLOG_CRIT, KLOG_SUBSYS_GENERIC, fmt, ##__VA_ARGS__)
-#define pr_err(fmt, ...) klog_ex(KLOG_ERR, KLOG_SUBSYS_GENERIC, fmt, ##__VA_ARGS__)
-#define pr_warn(fmt, ...) klog_ex(KLOG_WARN, KLOG_SUBSYS_GENERIC, fmt, ##__VA_ARGS__)
-#define pr_notice(fmt, ...) klog_ex(KLOG_NOTICE, KLOG_SUBSYS_GENERIC, fmt, ##__VA_ARGS__)
-#define pr_info(fmt, ...) klog_ex(KLOG_INFO, KLOG_SUBSYS_GENERIC, fmt, ##__VA_ARGS__)
-
-#define pr_emerg_s(subsys, fmt, ...) klog_ex(KLOG_EMERG, subsys, fmt, ##__VA_ARGS__)
-#define pr_alert_s(subsys, fmt, ...) klog_ex(KLOG_ALERT, subsys, fmt, ##__VA_ARGS__)
-#define pr_crit_s(subsys, fmt, ...) klog_ex(KLOG_CRIT, subsys, fmt, ##__VA_ARGS__)
-#define pr_err_s(subsys, fmt, ...) klog_ex(KLOG_ERR, subsys, fmt, ##__VA_ARGS__)
-#define pr_warn_s(subsys, fmt, ...) klog_ex(KLOG_WARN, subsys, fmt, ##__VA_ARGS__)
-#define pr_notice_s(subsys, fmt, ...) klog_ex(KLOG_NOTICE, subsys, fmt, ##__VA_ARGS__)
-#define pr_info_s(subsys, fmt, ...) klog_ex(KLOG_INFO, subsys, fmt, ##__VA_ARGS__)
-
-#ifdef KLOG_ENABLE_DEBUG
-#define pr_debug(fmt, ...) klog_ex(KLOG_DEBUG, KLOG_SUBSYS_GENERIC, fmt, ##__VA_ARGS__)
-#else
-#define pr_debug(fmt, ...) do {} while (0)
-#endif
-
-/* pr_debug_s: subsystem-tagged debug. Always compiled in (subsys is the gate);
- * level KLOG_DEBUG ensures messages stay in the ring buffer but are filtered
- * out of the console by default (console_level = KLOG_INFO). Toggle a subsys
- * with klog_enable_subsys()/klog_disable_subsys() at runtime — or, to make
- * the messages also reach the console for debugging, raise console_level
- * via klog_set_level(KLOG_DEBUG). */
-#define pr_debug_s(subsys, fmt, ...) \
- klog_ex(KLOG_DEBUG, subsys, fmt, ##__VA_ARGS__)
-
-/* Linux kernel loglevel prefixes */
-#define KERN_SOH "\001"
-#define KERN_EMERG KERN_SOH "0"
-#define KERN_ALERT KERN_SOH "1"
-#define KERN_CRIT KERN_SOH "2"
-#define KERN_ERR KERN_SOH "3"
-#define KERN_WARNING KERN_SOH "4"
-#define KERN_NOTICE KERN_SOH "5"
-#define KERN_INFO KERN_SOH "6"
-#define KERN_DEBUG KERN_SOH "7"
-
-/* Boot stage log (thin wrapper over printk) */
-#define BOOT_STAGE(n, desc) \
- printk(KERN_INFO "STAGE%u %s\n", (unsigned)(n), (desc))
-
-extern "C" [[noreturn]] void kernel_panic(const char* msg, const char* detail = nullptr);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/na/include/lume/kobject.h b/na/include/lume/kobject.h
deleted file mode 100644
index 8581d6e..0000000
--- a/na/include/lume/kobject.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * kobject — generic refcounted object with parent/child relationship.
- *
- * Mirrors Linux include/linux/kobject.h. Embedded into device, gendisk,
- * request_queue, block_device, etc. Each kobject has a kobj_type that
- * provides the release callback called when the last reference drops.
- *
- * sysfs integration is not yet implemented — the sd/sysfs_ops fields are
- * reserved for future use.
- *
- * Reference: docs/specs/storage_stack.md §4.2
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <lume/kref.h>
-#include <kernel/sync/spinlock.h>
-
-struct kobj_type;
-
-struct kobject {
- const char* name;
- kobject* parent;
- kref refcount;
- const kobj_type* ktype;
- list_node entry; // node in parent->children
- list_node children; // head of children list
- Spinlock lock;
- bool state_in_sysfs; // reserved, always false for now
-};
-
-struct kobj_type {
- void (*release)(kobject*);
- /* sysfs_ops + default_attrs: reserved for future sysfs implementation. */
-};
-
-/* Initialize a kobject in place; refcount starts at 1.
- * Caller must provide a non-null ktype with a valid release(). */
-void kobject_init(kobject* k, const kobj_type* type);
-
-/* Link `k` under `parent` (which may be null for a detached root).
- * Caller passes a stable `name` pointer (e.g. a static string or an
- * already-strdup'd buffer); kobject does not own the name string. */
-int kobject_add(kobject* k, kobject* parent, const char* name);
-
-/* Unlink from parent. Does NOT drop the final ref — caller decides
- * when to kobject_put(). */
-void kobject_del(kobject* k);
-
-/* Bump / drop reference. */
-void kobject_get(kobject* k);
-void kobject_put(kobject* k);
diff --git a/na/include/lume/kprintf.h b/na/include/lume/kprintf.h
deleted file mode 100644
index b158602..0000000
--- a/na/include/lume/kprintf.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Kernel Formatted Output — kprintf
- *
- * Provides printf-style formatted output for the kernel.
- * Built on top of stb_sprintf for full format string support
- * without any libc dependency.
- *
- * Output is synchronous and directed to the console (UART).
- * Safe to call from any context (interrupt or process).
- *
- * Usage:
- * kprintf("Hello %s, addr=0x%lx\n", name, addr);
- */
-
-#include <stdarg.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Formatted print to console (like printf). */
-__attribute__((format(printf, 1, 2)))
-int kprintf(const char* fmt, ...);
-
-/* Formatted print with va_list. */
-int kvprintf(const char* fmt, va_list va);
-
-/* Formatted snprintf into buffer. */
-__attribute__((format(printf, 3, 4)))
-int ksnprintf(char* buf, int count, const char* fmt, ...);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/na/include/lume/kref.h b/na/include/lume/kref.h
deleted file mode 100644
index 13d53f3..0000000
--- a/na/include/lume/kref.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * kref — atomic reference counting.
- *
- * Mirrors Linux include/linux/kref.h. Use kref_init() to set count=1,
- * kref_get() / kref_put() to add/drop refs. kref_put() invokes the
- * release callback when the final ref is dropped and returns 1 in that
- * case.
- *
- * Reference: docs/specs/storage_stack.md §4.1
- */
-
-#include <lume/types.h>
-#include <lume/atomic.h>
-
-struct kref {
- lume::atomic<int32> refcount;
-};
-
-inline void kref_init(kref* k)
-{
- k->refcount.store(1, __ATOMIC_RELEASE);
-}
-
-inline void kref_get(kref* k)
-{
- k->refcount.fetch_add(1, __ATOMIC_RELAXED);
-}
-
-inline int kref_put(kref* k, void (*release)(kref*))
-{
- if (k->refcount.fetch_sub(1, __ATOMIC_ACQ_REL) == 1) {
- release(k);
- return 1;
- }
- return 0;
-}
-
-inline int32 kref_read(const kref* k)
-{
- return k->refcount.load(__ATOMIC_RELAXED);
-}
diff --git a/na/include/lume/libfdt.h b/na/include/lume/libfdt.h
deleted file mode 100644
index e3496e9..0000000
--- a/na/include/lume/libfdt.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _INCLUDE_LIBFDT_H_
-#define _INCLUDE_LIBFDT_H_
-
-#include <lume/libfdt_env.h>
-#include "../../scripts/dtc/libfdt/libfdt.h"
-
-#endif /* _INCLUDE_LIBFDT_H_ */
diff --git a/na/include/lume/libfdt_env.h b/na/include/lume/libfdt_env.h
deleted file mode 100644
index a95fb1d..0000000
--- a/na/include/lume/libfdt_env.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef LIBFDT_ENV_H
-#define LIBFDT_ENV_H
-
-/*
- * Kernel-side adapter for the standalone libfdt sources in
- * scripts/dtc/libfdt/. libfdt's *.c files start with
- * #include "libfdt_env.h"
- * and expect this header to provide:
- * - byte-swap helpers (cpu_to_fdt16/32/64, fdt16/32/64_to_cpu)
- * - fdt16/32/64_t typedefs (just plain uint types — endianness is
- * handled in the helpers above)
- * - string / mem helpers (memcpy, memmove, memcmp, strlen, strcmp,
- * strchr, strnlen, ...)
- * - INT_MAX, UINT32_MAX (libfdt bounds-checks props with these)
- * - size_t / uintptr_t (everywhere)
- *
- * LumeOS-native: no Linux <linux/...> includes. Provides the surface
- * libfdt needs and nothing else.
- */
-
-#include <lume/types.h> /* uint16_t / uint32_t / uint64_t / size_t */
-#include <lume/limits.h> /* INT_MAX */
-#include <string.h> /* libc-shim memcpy / memcmp / strlen ... */
-
-/* libfdt uses these names; both come from <lume/limits.h>. */
-#define INT32_MAX 2147483647
-#define UINT32_MAX 4294967295U
-
-/* libfdt's "FDT word" types — opaque big-endian-stored u16/u32/u64.
- * The byte-swap helpers below are the only access path. */
-typedef uint16_t fdt16_t;
-typedef uint32_t fdt32_t;
-typedef uint64_t fdt64_t;
-
-/*
- * Endianness helpers. FDT (Device Tree Blob) is big-endian by spec.
- * LumeOS runs on RISC-V (little-endian) and x86 (little-endian); we
- * unconditionally byte-swap.
- *
- * Open-coded shifts rather than __builtin_bswapN — on RISC-V without
- * the Zbb extension, gcc lowers __builtin_bswap32 to a libgcc call
- * (__bswapsi2 / __bswapdi2) which isn't present in a freestanding
- * kernel link. Mirrors Linux's scripts/dtc/libfdt/libfdt_env.h
- * which uses ___constant_swab32-style explicit shifts. */
-static inline uint16_t __lumefdt_bswap16(uint16_t x)
-{
- return (uint16_t)((x << 8) | (x >> 8));
-}
-static inline uint32_t __lumefdt_bswap32(uint32_t x)
-{
- return ((x & 0x000000FFu) << 24) |
- ((x & 0x0000FF00u) << 8) |
- ((x & 0x00FF0000u) >> 8) |
- ((x & 0xFF000000u) >> 24);
-}
-static inline uint64_t __lumefdt_bswap64(uint64_t x)
-{
- return ((x & 0x00000000000000FFull) << 56) |
- ((x & 0x000000000000FF00ull) << 40) |
- ((x & 0x0000000000FF0000ull) << 24) |
- ((x & 0x00000000FF000000ull) << 8) |
- ((x & 0x000000FF00000000ull) >> 8) |
- ((x & 0x0000FF0000000000ull) >> 24) |
- ((x & 0x00FF000000000000ull) >> 40) |
- ((x & 0xFF00000000000000ull) >> 56);
-}
-
-#define fdt16_to_cpu(x) __lumefdt_bswap16((uint16_t)(x))
-#define cpu_to_fdt16(x) ((fdt16_t)__lumefdt_bswap16((uint16_t)(x)))
-#define fdt32_to_cpu(x) __lumefdt_bswap32((uint32_t)(x))
-#define cpu_to_fdt32(x) ((fdt32_t)__lumefdt_bswap32((uint32_t)(x)))
-#define fdt64_to_cpu(x) __lumefdt_bswap64((uint64_t)(x))
-#define cpu_to_fdt64(x) ((fdt64_t)__lumefdt_bswap64((uint64_t)(x)))
-
-#endif /* LIBFDT_ENV_H */
diff --git a/na/include/lume/limits.h b/na/include/lume/limits.h
deleted file mode 100644
index a275a71..0000000
--- a/na/include/lume/limits.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _LUME_LIMITS_H
-#define _LUME_LIMITS_H
-
-/*
- * Numeric limit constants — minimal subset needed by LumeOS kernel code.
- *
- * Values match the standard <limits.h> on all 64-bit LP64 targets, which
- * is the only ABI LumeOS supports. Defined as plain integer literals so
- * this header is usable from C, C++, and assembly without dragging in
- * the toolchain's <limits.h> (which on hosted builds references libc
- * internals we don't link against).
- */
-
-/* Signed/unsigned char */
-#define SCHAR_MIN (-128)
-#define SCHAR_MAX 127
-#define UCHAR_MAX 255
-#define CHAR_MIN SCHAR_MIN
-#define CHAR_MAX SCHAR_MAX
-
-/* Short */
-#define SHRT_MIN (-32768)
-#define SHRT_MAX 32767
-#define USHRT_MAX 65535
-
-/* Int */
-#define INT_MIN (-INT_MAX - 1)
-#define INT_MAX 2147483647
-#define UINT_MAX 4294967295U
-
-/* Long (LP64: 64-bit) */
-#define LONG_MIN (-LONG_MAX - 1L)
-#define LONG_MAX 9223372036854775807L
-#define ULONG_MAX 18446744073709551615UL
-
-/* Long long */
-#define LLONG_MIN (-LLONG_MAX - 1LL)
-#define LLONG_MAX 9223372036854775807LL
-#define ULLONG_MAX 18446744073709551615ULL
-
-/* size_t (LP64) */
-#define SIZE_MAX ULONG_MAX
-#define SSIZE_MAX LONG_MAX
-
-#endif /* _LUME_LIMITS_H */
diff --git a/na/include/lume/list.h b/na/include/lume/list.h
deleted file mode 100644
index ed1202a..0000000
--- a/na/include/lume/list.h
+++ /dev/null
@@ -1,346 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-#include <lume/types.h>
-#include <lume/container_of.h>
-
-/**
- * @brief The node to be embedded in a struct to make it linkable.
- *
- * An object can be on multiple lists simultaneously by embedding multiple
- * list_node members with different names.
- */
-struct list_node {
- list_node* prev;
- list_node* next;
-
- /**
- * @brief Initializes the node to point to itself, forming an empty list head.
- */
- void init() {
- prev = this;
- next = this;
- }
-
- /**
- * @brief Checks if the list this node is the head of is empty.
- * @return True if the list is empty, false otherwise.
- */
- bool is_empty() const {
- return next == this;
- }
-};
-
-/*
- * Type-safe container_of: given a pointer to a list_node member,
- * recover a pointer to the enclosing object.
- * Uses pointer-to-member arithmetic — no __builtin_offsetof needed.
- */
-template <typename T, list_node T::*Member>
-static inline T *list_entry(list_node *node)
-{
- const size_t off = (size_t)(uintptr) & (((T *)nullptr)->*Member);
- return reinterpret_cast<T *>(reinterpret_cast<uintptr>(node) - off);
-}
-
-/**
- * @brief A type-safe, intrusive, doubly-linked list.
- *
- * This class manages objects of type T that have a `list_node` member.
- * It has zero runtime overhead compared to a manual C implementation and
- * provides compile-time type safety.
- *
- * @tparam T The type of the object in the list.
- * @tparam NodeMember A pointer-to-member specifying which `list_node` in T to use.
- */
-template <typename T, list_node T::*NodeMember>
-class List {
-private:
- static list_node* get_node(T* obj) {
- return &(obj->*NodeMember);
- }
-
- // NOTE: `((T*)nullptr)->*NodeMember` is technically undefined behavior in
- // standard C++. However, it's a common idiom in low-level programming
- // (similar to `offsetof`) that compilers typically handle correctly,
- // especially with flags like `-fno-delete-null-pointer-checks`.
- // The alternative (using `offsetof`) would sacrifice the compile-time
- // type safety of the pointer-to-member template parameter.
- static T* get_obj(list_node* node) {
- constexpr size_t offset = (size_t)(uintptr) & (((T*)nullptr)->*NodeMember);
- return reinterpret_cast<T*>(reinterpret_cast<uintptr>(node) - offset);
- }
-
- static const T* get_obj(const list_node* node) {
- constexpr size_t offset = (size_t)(uintptr) & (((T*)nullptr)->*NodeMember);
- return reinterpret_cast<const T*>(reinterpret_cast<uintptr>(node) - offset);
- }
-
-public:
- class iterator;
- class const_iterator;
-
- class iterator {
- friend class List<T, NodeMember>;
- friend class const_iterator;
-
- public:
- iterator(list_node* node) : node_(node) {}
-
- T& operator*() const { return *get_obj(node_); }
- T* operator->() const { return get_obj(node_); }
-
- iterator& operator++() {
- node_ = node_->next;
- return *this;
- }
- iterator operator++(int) {
- iterator tmp = *this;
- ++(*this);
- return tmp;
- }
-
- iterator& operator--() {
- node_ = node_->prev;
- return *this;
- }
- iterator operator--(int) {
- iterator tmp = *this;
- --(*this);
- return tmp;
- }
-
- bool operator==(const iterator& other) const { return node_ == other.node_; }
- bool operator!=(const iterator& other) const { return !(*this == other); }
-
- private:
- list_node* node_;
- };
-
- class const_iterator {
- public:
- const_iterator(const list_node* node) : node_(node) {}
- const_iterator(const iterator& other) : node_(other.node_) {}
-
- const T& operator*() const { return *get_obj(node_); }
- const T* operator->() const { return get_obj(node_); }
-
- const_iterator& operator++() {
- node_ = node_->next;
- return *this;
- }
- const_iterator operator++(int) {
- const_iterator tmp = *this;
- ++(*this);
- return tmp;
- }
-
- const_iterator& operator--() {
- node_ = node_->prev;
- return *this;
- }
- const_iterator operator--(int) {
- const_iterator tmp = *this;
- --(*this);
- return tmp;
- }
-
- bool operator==(const const_iterator& other) const { return node_ == other.node_; }
- bool operator!=(const const_iterator& other) const { return !(*this == other); }
-
- private:
- const list_node* node_;
- };
-
- List() { head_.init(); }
- ~List() = default;
-
- // This container is intrusive and does not own the objects.
- // Copying or moving it is a logical error.
- List(const List&) = delete;
- List& operator=(const List&) = delete;
-
- bool empty() const { return head_.is_empty(); }
-
- void push_front(T* obj) {
- list_node* node = get_node(obj);
- node->next = head_.next;
- node->prev = &head_;
- head_.next->prev = node;
- head_.next = node;
- }
-
- void push_back(T* obj) {
- list_node* node = get_node(obj);
- node->prev = head_.prev;
- node->next = &head_;
- head_.prev->next = node;
- head_.prev = node;
- }
-
- /**
- * @brief Splices all elements from another list to the front of this list.
- *
- * Moves all elements from `other` to the beginning of `this` list in O(1)
- * time. The `other` list becomes empty after the operation.
- *
- * @param other The list to splice from.
- */
- void splice_front(List& other) {
- if (other.empty()) {
- return;
- }
- list_node* this_first = head_.next;
- list_node* other_first = other.head_.next;
- list_node* other_last = other.head_.prev;
-
- // Link other's last to this's original first
- other_last->next = this_first;
- this_first->prev = other_last;
-
- // Link this's head to other's first
- head_.next = other_first;
- other_first->prev = &head_;
-
- // Reset the other list
- other.head_.init();
- }
-
- /**
- * @brief Splices all elements from another list to the back of this list.
- *
- * Moves all elements from `other` to the end of `this` list in O(1)
- * time. The `other` list becomes empty after the operation.
- *
- * @param other The list to splice from.
- */
- void splice_tail(List& other) {
- if (other.empty()) {
- return;
- }
- list_node* this_last = head_.prev;
- list_node* other_first = other.head_.next;
- list_node* other_last = other.head_.prev;
-
- // Link this's last to other's first
- this_last->next = other_first;
- other_first->prev = this_last;
-
- // Link other's last to this's head
- other_last->next = &head_;
- head_.prev = other_last;
-
- // Reset the other list
- other.head_.init();
- }
-
- T* pop_front() {
- if (empty()) {
- return nullptr;
- }
- list_node* node = head_.next;
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->init();
- return get_obj(node);
- }
-
- T* pop_back() {
- if (empty()) {
- return nullptr;
- }
- list_node* node = head_.prev;
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->init();
- return get_obj(node);
- }
-
- void remove(T* obj) {
- list_node* node = get_node(obj);
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->init();
- }
-
- T* front() {
- return empty() ? nullptr : get_obj(head_.next);
- }
-
- const T* front() const {
- return empty() ? nullptr : get_obj(head_.next);
- }
-
- T* back() {
- return empty() ? nullptr : get_obj(head_.prev);
- }
-
- const T* back() const {
- return empty() ? nullptr : get_obj(head_.prev);
- }
-
- iterator begin() { return iterator(head_.next); }
- iterator end() { return iterator(&head_); }
- const_iterator begin() const { return const_iterator(head_.next); }
- const_iterator end() const { return const_iterator(&head_); }
- const_iterator cbegin() const { return const_iterator(head_.next); }
- const_iterator cend() const { return const_iterator(&head_); }
-
-private:
- list_node head_;
-};
-
-/* ========================================================================
- * Linux-compatible C-style list macros (for VFS subsystem)
- * ======================================================================== */
-
-using list_head = list_node;
-
-inline void list_init(list_node* head) {
- head->prev = head;
- head->next = head;
-}
-
-inline bool list_empty(const list_node* head) {
- return head->next == head;
-}
-
-inline void __list_add(list_node* node, list_node* p, list_node* n) {
- n->prev = node;
- node->next = n;
- node->prev = p;
- p->next = node;
-}
-
-inline void list_add(list_node* node, list_node* head) {
- __list_add(node, head, head->next);
-}
-
-inline void list_add_tail(list_node* node, list_node* head) {
- __list_add(node, head->prev, head);
-}
-
-inline void list_del(list_node* node) {
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = nullptr;
- node->prev = nullptr;
-}
-
-inline void list_del_init(list_node* node) {
- node->prev->next = node->next;
- node->next->prev = node->prev;
- list_init(node);
-}
-
-#define list_entry(ptr, type, member) container_of(ptr, type, member)
-
-#define list_for_each(pos, head) \
- for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next)
-
-#define list_for_each_safe(pos, tmp, head) \
- for ((pos) = (head)->next, (tmp) = (pos)->next; \
- (pos) != (head); \
- (pos) = (tmp), (tmp) = (pos)->next)
diff --git a/na/include/lume/lumeos.lds.h b/na/include/lume/lumeos.lds.h
deleted file mode 100644
index 58d2f72..0000000
--- a/na/include/lume/lumeos.lds.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Common Linker Script Macros (Machine-Independent)
- *
- * Shared section definitions that every architecture must emit.
- * Architecture-specific lumeos.lds.S files #include this header
- * and use these macros to avoid duplicating common boilerplate.
- *
- * Modeled after Linux include/asm-generic/vmlinux.lds.h
- * Reference: docs/specs/linker_and_entry.md
- */
-
-#ifndef _LUME_LUMEOS_LDS_H
-#define _LUME_LUMEOS_LDS_H
-
-/* ===== Text Section: executable code =====
- *
- * .init.text is merged into the main .text segment so PC-relative `la`
- * from regular code can call __init helpers without a thunk. Linux
- * keeps a separate .init.text only to reclaim the pages after boot —
- * LumeOS doesn't yet reclaim, so the merge is harmless and avoids an
- * extra PT_LOAD entry. */
-#define TEXT_SECTION \
- .text : AT(ADDR(.text) - PAGE_OFFSET) { \
- _stext = .; \
- *(.text .text.*) \
- *(.init.text .init.text.*) \
- *(.fixup) \
- _etext = .; \
- } = 0
-
-/* ===== Read-Only Data + C++ init_array + initramfs =====
- *
- * Layout invariants:
- * - extable / init_array / driver_array / initramfs all live INSIDE
- * .rodata between _srodata and _erodata so the VMM has one R-only
- * mapping to manage.
- * - The relocation table (.lume.reloc) is NOT in here — it lives in
- * its own padded output section (see RELOC_SECTION) so that its
- * two-pass-build size variance can't shift downstream offsets.
- * (Putting it in .rodata caused a heisenbug where pre-link offsets
- * used by gen_reloc.py didn't match post-link offsets after the
- * reloc.o blob was inserted.)
- */
-#define RODATA_SECTION \
- .rodata : AT(ADDR(.rodata) - PAGE_OFFSET) { \
- _srodata = .; \
- *(.rodata .rodata.*) \
- *(.srodata .srodata.* .srodata.cst*) \
- . = ALIGN(8); \
- __extable_start = .; \
- KEEP(*(.extable)) \
- __extable_end = .; \
- INIT_ARRAY \
- DRIVER_ARRAY \
- /* Embedded initramfs CPIO archive */ \
- . = ALIGN(8); \
- __initramfs_start = .; \
- KEEP(*(.init.ramfs)) \
- __initramfs_end = .; \
- . = ALIGN(4096); \
- _erodata = .; \
- }
-
-/* ===== .rel.dyn / .rela.dyn — Linker-emitted RELA relocations =====
- *
- * Mirrors arch/riscv/kernel/vmlinux.lds.S §101-109: the .rela.dyn
- * section sits BETWEEN init data and .rodata so it lands in a
- * read-only PT_LOAD segment alongside .text/.rodata. The companion
- * dynamic-linker bookkeeping (.dynamic, .dynsym, .dynstr, .hash,
- * .gnu.hash) lives in the RW segment AFTER .data — Linux does the
- * same under #ifdef CONFIG_RELOCATABLE (§139-147). */
-#define RELA_DYN_SECTION \
- .rel.dyn : AT(ADDR(.rel.dyn) - PAGE_OFFSET) { \
- *(.rel.dyn*) \
- } \
- .rela.dyn : AT(ADDR(.rela.dyn) - PAGE_OFFSET) ALIGN(8) {\
- __rela_dyn_start = .; \
- *(.rela .rela*) \
- __rela_dyn_end = .; \
- }
-
-/* ===== GOT — kept; everything else dynamic-linker is discarded =====
- *
- * Strict mirror of arch/riscv/kernel/vmlinux.lds.S in Linux: the only
- * dynamic-linker artefact the kernel keeps is `.got` (it's referenced
- * by every -fPIE compiled global access). `.plt .dynamic .dynsym
- * .dynstr .hash .gnu.hash .interp .dynbss` are all sent to /DISCARD/
- * via the DISCARDS macro below — the kernel never invokes a dynamic
- * linker (head.S walks .rela.dyn directly).
- *
- * Keeping .plt was the cause of two distinct bugs:
- * 1. PLT trampoline target for an undefined symbol (selftest_reloc
- * after we removed test_reloc.cc) was unresolved → jumps to a
- * slot that re-jumps to itself → instruction page fault.
- * 2. .plt carries X permission; living in the same PT_LOAD as .data
- * forces the load segment to RWX, which `ld` warns about.
- *
- * Sit in the same PT_LOAD as .data because .got is RW. */
-#define GOT_SECTION \
- .got : AT(ADDR(.got) - PAGE_OFFSET) { *(.got*) }
-
-/* ===== Embedded initramfs (CPIO archive) ===== */
-#define INITRAMFS_SECTION \
- .init.ramfs : AT(ADDR(.init.ramfs) - PAGE_OFFSET) { \
- . = ALIGN(8); \
- __initramfs_start = .; \
- KEEP(*(.init.ramfs)) \
- __initramfs_end = .; \
- }
-
-/* ===== C++ Global Constructor Table ===== */
-#define INIT_ARRAY \
- . = ALIGN(8); \
- __init_array_start = .; \
- KEEP(*(.init_array .init_array.*)) \
- __init_array_end = .;
-
-/* ===== Driver Registration Table ===== */
-#define DRIVER_ARRAY \
- . = ALIGN(8); \
- PROVIDE(__drivers_start = .); \
- KEEP(*(.drivers)) \
- PROVIDE(__drivers_end = .);
-
-/* ===== Initialized Read-Write Data =====
- *
- * .init.data is merged into .data for the same reason TEXT_SECTION
- * absorbs .init.text — LumeOS doesn't yet reclaim __init pages, so
- * splitting them gains nothing while costing a PT_LOAD entry. */
-#define DATA_SECTION \
- .data : AT(ADDR(.data) - PAGE_OFFSET) { \
- _sdata = .; \
- *(.data .data.*) \
- *(.init.data .init.data.*) \
- ARCH_DATA_EXTRA \
- _edata = .; \
- }
-
-/* ===== BSS: Uninitialized Data ===== */
-#define BSS_SECTION \
- .bss : AT(ADDR(.bss) - PAGE_OFFSET) { \
- _sbss = .; \
- *(.bss .bss.*) \
- *(.sbss .sbss.*) \
- ARCH_BSS_EXTRA \
- _ebss = .; \
- }
-
-/* ===== Discard Unwanted Sections =====
- *
- * Mirrors arch/riscv/kernel/vmlinux.lds.S in Linux:
- * /DISCARD/ : {
- * *(.dynbss)
- * *(.dynstr*)
- * *(.dynamic*)
- * *(.plt*)
- * *(.interp*)
- * *(.eh_frame)
- * *(.note.GNU-stack)
- * }
- *
- * Why each is safe to discard:
- * .plt — -Bsymbolic resolves all internal refs at link time;
- * any remaining PLT entry is an undefined symbol that
- * the kernel would have crashed on anyway.
- * .dynamic etc. — kernel doesn't invoke a dynamic linker; head.S
- * walks __rela_dyn_start..__rela_dyn_end manually.
- * .interp — no PT_INTERP at runtime.
- * .eh_frame* — no C++ exceptions in kernel (-fno-exceptions).
- * .note.* — boot-time identification metadata, unused. */
-#define DISCARDS \
- /DISCARD/ : { \
- *(.dynbss) \
- *(.dynstr*) \
- *(.dynamic*) \
- *(.dynsym*) \
- *(.hash*) *(.gnu.hash*) \
- *(.plt*) \
- *(.interp*) \
- *(.eh_frame .eh_frame_hdr) \
- *(.note .note.*) \
- *(.comment) \
- }
-
-/* ===== Kernel Image Boundary ===== */
-#define KERNEL_END \
- . = ALIGN(4096); \
- _kernel_end = .;
-
-#endif /* _LUME_LUMEOS_LDS_H */
diff --git a/na/include/lume/memblock.h b/na/include/lume/memblock.h
deleted file mode 100644
index cb9a76b..0000000
--- a/na/include/lume/memblock.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * memblock — Boot-time physical memory allocator
- *
- * A simple region-based allocator active from early boot until
- * the buddy system (PMM) takes over. Inspired by Linux's memblock.
- *
- * Lifetime:
- * 1. memblock_init() — discovers RAM via FDT, reserves kernel image
- * 2. vmm_init() — uses memblock_alloc for page table pages & page_map
- * 3. pmm_init() — absorbs remaining free regions into buddy
- * 4. memblock_retire() — marks memblock inactive; all alloc via PMM
- *
- * This eliminates the need for post-init pointer relocation when
- * rebinding page_map to a VMEMMAP virtual address.
- */
-
-#include <lume/types.h>
-
-inline constexpr int kMemblockMaxRegions = 128;
-
-/* Initialize memblock: scan FDT for RAM, reserve kernel image + firmware. */
-void memblock_init(uint64 fdt_paddr);
-
-/* Mark a physical range as reserved (will not be handed to buddy). */
-void memblock_reserve(uint64 base, uint64 size);
-
-/* Allocate `size` bytes aligned to `align` from top of free memory.
- * Returns PA, or 0 on failure. Used for page_map. */
-uint64 memblock_alloc_top(uint64 size, uint64 align);
-
-/* Allocate `size` bytes aligned to `align` from bottom of free memory.
- * Returns PA, or 0 on failure. Used for page table pages. */
-uint64 memblock_alloc(uint64 size, uint64 align);
-
-/* Query memory layout (same info as FDT). */
-uint64 memblock_get_mem_base();
-uint64 memblock_get_mem_size();
-
-/* Is memblock still the active allocator? */
-bool memblock_is_active();
-
-/* Mark memblock as retired (PMM takes over). */
-void memblock_retire();
-
-/* Iterate all free (unreserved) regions in ascending order.
- * Used by pmm_init() to feed free pages into the buddy system. */
-void memblock_for_each_free(void (*cb)(uint64 base, uint64 size, void* ctx),
- void* ctx);
diff --git a/na/include/lume/mm_layout.h b/na/include/lume/mm_layout.h
deleted file mode 100644
index 7744de0..0000000
--- a/na/include/lume/mm_layout.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * mm_layout.h — User/Kernel address space boundary definitions
- *
- * Single source of truth for the user/kernel VA split.
- * All modules (VMM, uaccess, page fault, tests) MUST include this
- * header instead of defining their own constants.
- *
- * Layout (SV39, RISC-V):
- * [0x0000_0000_0000, kUserVaLimit) — User space
- * [kUserVaLimit, 0xFFFF_FFFF_FFFF) — Kernel space (direct map, vmemmap, etc.)
- *
- * Reference: docs/specs/linux_abi_mm_plan.md §5
- */
-
-#include <lume/types.h>
-#include <lume/config.h>
-
-/* User VA range: [kUserVaBase, kUserVaLimit)
- * kUserVaBase avoids mapping the null page (catch NULL derefs).
- *
- * SV39: user space is the lower half [0, 2^38). The upper half
- * [0xFFFFFFC000000000, 0xFFFFFFFFFFFFFFFF] is kernel space.
- * Addresses between 2^38 and 0xFFFFFFC000000000 are non-canonical
- * and will always fault — they must never be used.
- *
- * This value MUST match the page table mode selected by the MD layer.
- * If we ever support SV48/SV57, update this accordingly. */
-inline constexpr uint64 kUserVaBase = kPageSize;
-inline constexpr uint64 kUserVaLimit = 0x4000000000ULL; /* 2^38 = 256 GB */
-
-/* Kernel VA starts at the sign-extended upper half */
-inline constexpr uint64 kKernelVaBase = 0xFFFFFFC000000000ULL;
-
-/* User stack: sits at the top of the user VA range.
- * Matches Linux STACK_TOP = TASK_SIZE = kUserVaLimit.
- * One guard page below the top, 8 MB stack. */
-inline constexpr uint64 kUserStackSize = 8ULL * 1024 * 1024;
-inline constexpr uint64 kUserStackTop = kUserVaLimit - kPageSize; /* guard page */
-inline constexpr uint64 kUserStackBase = kUserStackTop - kUserStackSize;
-
-/*
- * Check whether an address range [addr, addr+len) is entirely
- * within user space. Returns true if valid.
- */
-inline bool is_user_range(uint64 addr, uint64 len)
-{
- if (addr < kUserVaBase) {
- return false;
- }
- if (addr >= kUserVaLimit) {
- return false;
- }
- if (addr + len < addr) {
- return false; // Overflow
- }
- if (addr + len > kUserVaLimit) {
- return false;
- }
- return true;
-}
-
-/*
- * Check whether a single address is in user space.
- */
-inline bool is_user_address(uint64 addr)
-{
- return addr >= kUserVaBase && addr < kUserVaLimit;
-}
diff --git a/na/include/lume/mman.h b/na/include/lume/mman.h
deleted file mode 100644
index d952419..0000000
--- a/na/include/lume/mman.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * POSIX Memory Management Constants
- * Reference: docs/specs/syscall.md §9
- */
-
-/* mmap / mprotect protection flags */
-inline constexpr int PROT_NONE = 0x0;
-inline constexpr int PROT_READ = 0x1;
-inline constexpr int PROT_WRITE = 0x2;
-inline constexpr int PROT_EXEC = 0x4;
-
-/* mmap flags */
-inline constexpr int MAP_SHARED = 0x01;
-inline constexpr int MAP_PRIVATE = 0x02;
-inline constexpr int MAP_FIXED = 0x10;
-inline constexpr int MAP_ANONYMOUS = 0x20;
diff --git a/na/include/lume/mount.h b/na/include/lume/mount.h
deleted file mode 100644
index 13f8e62..0000000
--- a/na/include/lume/mount.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * mount.h — Internal mount structure
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <lume/atomic.h>
-#include <lume/file.h>
-
-struct dentry;
-struct super_block;
-struct file_system_type;
-
-/* ================================================================== */
-/* mount (VFS-internal) */
-/* ================================================================== */
-
-struct mount {
- vfsmount mnt;
- mount* mnt_parent;
- dentry* mnt_mountpoint;
- list_node mnt_child;
- list_head mnt_mounts;
- list_node mnt_list;
- lume::atomic<int32> mnt_count;
-};
-
-inline mount* real_mount(vfsmount* m) {
- return reinterpret_cast<mount*>(m);
-}
-
-/* ================================================================== */
-/* Mount API */
-/* ================================================================== */
-
-int do_mount(const char* dev, const char* dir,
- const char* fstype, uint64 flags, void* data);
-int do_umount(vfsmount* mnt, int flags);
-
-vfsmount* vfs_kern_mount(file_system_type* type, int flags,
- const char* name, void* data);
-
-/* Global root */
-extern vfsmount* g_root_mnt;
-extern dentry* g_root_dentry;
-
-void mount_init();
diff --git a/na/include/lume/mutex.h b/na/include/lume/mutex.h
deleted file mode 100644
index 9719aa6..0000000
--- a/na/include/lume/mutex.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Mutex — Linux-style sleeping mutex (no priority inheritance).
- *
- * Semantics mirror Linux kernel/locking/mutex.c:
- * - Single owner (nullptr when free).
- * - FIFO wait list — first waiter is the next owner.
- * - Recursive acquire by the same task is FORBIDDEN (panic).
- * - Owner MUST be the one to unlock.
- * - lock() may sleep; MUST NOT be called from IRQ context, with
- * preemption disabled, or while holding a spinlock.
- * - try_lock() never sleeps; may be called from any context.
- *
- * Priority inheritance lives in a separate primitive (RtMutex, future)
- * just like Linux splits struct mutex / struct rt_mutex.
- *
- * Reference: docs/specs/mutex.md
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <kernel/sync/spinlock.h>
-
-struct TaskControlBlock;
-
-class Mutex {
-public:
- void init(const char* name);
-
- /* Blocking acquire (uninterruptible). */
- void lock();
-
- /* Non-blocking attempt. Returns true if acquired. */
- bool try_lock();
-
- /* Release; only the owner may call (else panic). */
- void unlock();
-
- bool is_locked() const { return owner_ != nullptr; }
- bool is_owned_by_current() const;
- TaskControlBlock* owner() const { return owner_; }
-
-private:
- TaskControlBlock* owner_ = nullptr;
- Spinlock wait_lock_;
- list_node wait_list_{};
- const char* name_ = nullptr;
-};
-
-/* RAII guard. */
-class MutexGuard {
-public:
- explicit MutexGuard(Mutex& m) : m_(m) { m_.lock(); }
- ~MutexGuard() { m_.unlock(); }
- MutexGuard(const MutexGuard&) = delete;
- MutexGuard& operator=(const MutexGuard&) = delete;
-private:
- Mutex& m_;
-};
diff --git a/na/include/lume/namei.h b/na/include/lume/namei.h
deleted file mode 100644
index 1764693..0000000
--- a/na/include/lume/namei.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * namei.h — Pathname lookup (path resolution)
- */
-
-#include <lume/types.h>
-#include <lume/file.h> /* struct path */
-#include <lume/dcache.h> /* struct qstr */
-
-/* ================================================================== */
-/* LOOKUP_* flags */
-/* ================================================================== */
-
-inline constexpr uint32 LOOKUP_FOLLOW = 0x0001;
-inline constexpr uint32 LOOKUP_DIRECTORY = 0x0002;
-inline constexpr uint32 LOOKUP_CREATE = 0x0200;
-inline constexpr uint32 LOOKUP_EXCL = 0x0400;
-inline constexpr uint32 LOOKUP_PARENT = 0x0010;
-
-/* ================================================================== */
-/* Path lookup API */
-/* ================================================================== */
-
-int kern_path(const char* name, unsigned int flags, path* result);
-int filename_lookup(int dfd, const char* name, unsigned int flags, path* result);
-dentry* lookup_one_len(const char* name, dentry* parent, int len);
-
-void namei_init();
diff --git a/na/include/lume/new.h b/na/include/lume/new.h
deleted file mode 100644
index cdcacb8..0000000
--- a/na/include/lume/new.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Placement New for Freestanding Environments
- *
- * In -ffreestanding -nostdlib mode, <new> may not be available.
- * LumeOS provides its own placement new/delete operators.
- *
- * Usage: g_obj = new (g_obj_buf) MyClass(args...);
- *
- * Reference: docs/specs/global_init.md §3.1
- */
-
-inline void* operator new(decltype(sizeof(0)), void* ptr) noexcept { return ptr; }
-inline void operator delete(void*, void*) noexcept {}
diff --git a/na/include/lume/page.h b/na/include/lume/page.h
deleted file mode 100644
index 61bff95..0000000
--- a/na/include/lume/page.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Page — Physical page descriptor
- *
- * Each Page describes one 4KB physical page. A flat array of Page
- * structs (page_map) covers all physical memory discovered via FDT.
- *
- * Memory layout is compressed using a union: only one of free/slab
- * metadata is valid at a time, determined by `type`.
- *
- * Fields:
- * refcount — lifecycle reference count (atomic). Page cannot be
- * freed while refcount > 0.
- * mapcount — PTE mapping count (atomic). Number of page table
- * entries pointing to this page. Used by COW and
- * future reclaim.
- * flags — page state bits (PG_*). Also serves as a bit-lock
- * via PG_LOCKED for short critical sections.
- * type — discriminant for the union (Free/Slab/Anon/File).
- * mapping — pointer to reverse-mapping context. For anonymous
- * pages, points to AnonVma (low bit set). For file
- * pages, points to FileMapping (low bit clear).
- * index — page offset within the mapping object.
- *
- * Reference: docs/specs/pmm.md §3.1
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <lume/atomic.h>
-
-/* Forward declarations */
-class KmemCache;
-
-/* ================================================================== */
-/* Page type discriminant */
-/* ================================================================== */
-
-enum class PageType : uint8 {
- Free, // In Buddy free list
- PcpCached, // In Per-CPU cache (invisible to buddy coalescing)
- Allocated, // Handed out but not yet typed (PMM default)
- Slab, // Backing a Slab cache
- Anon, // Anonymous user page
- File, // File-backed page (page cache)
-};
-
-/* ================================================================== */
-/* Page flags (stored as atomic uint32) */
-/* ================================================================== */
-
-inline constexpr uint32 kPgLocked = (1U << 0); // Bit-lock for short critical sections
-inline constexpr uint32 kPgReferenced = (1U << 1); // Accessed since last check
-inline constexpr uint32 kPgDirty = (1U << 2); // Modified, needs writeback
-inline constexpr uint32 kPgWriteback = (1U << 3); // IO writeback in progress
-inline constexpr uint32 kPgLru = (1U << 4); // On an LRU list
-inline constexpr uint32 kPgMapped = (1U << 5); // Has at least one PTE mapping
-inline constexpr uint32 kPgSwapcache = (1U << 6); // In swap cache (future)
-inline constexpr uint32 kPgCow = (1U << 7); // Involved in COW sharing
-
-/* ================================================================== */
-/* Page descriptor */
-/* ================================================================== */
-
-struct Page {
- /* --- Counts (atomic, lock-free) --- */
- lume::atomic<uint32> refcount; // Lifecycle ref: 0 = reclaimable
- lume::atomic<uint32> mapcount; // PTE mapping count: 0 = no user mapping
-
- /* --- State --- */
- lume::atomic<uint32> flags; // PG_* bits (also serves as bit-lock)
- PageType type; // Discriminant for union
- uint8 order; // Buddy order (0–10, i.e. 4KB–4MB)
-
- /* --- Reverse mapping --- */
- void* mapping; // AnonVma* (low bit set) or FileMapping*
- uint64 index; // Page offset within mapping object
-
- /* --- LRU (future) --- */
- list_node lru; // LRU list linkage (for reclaim)
-
- /* --- Subsystem-specific metadata --- */
- union {
- /* Valid when type == Free */
- struct {
- list_node free_link; // Link into Buddy or PCP free list
- } free;
-
- /* Valid when type == Slab */
- struct {
- KmemCache* cache; // Owning slab cache
- uint32 obj_count; // Number of allocated objects on this page
- void* freelist; // Head of in-page free object linked list
- } slab;
-
- /* type == Anon / File: no extra union metadata (rmap via mapping) */
- };
-};
-
-/* ================================================================== */
-/* Page bit-lock (lightweight, no sleep, used for rmap critical */
-/* sections and short metadata updates) */
-/* ================================================================== */
-
-inline bool page_trylock(Page* page)
-{
- uint32 old = page->flags.fetch_or(kPgLocked);
- return (old & kPgLocked) == 0;
-}
-
-inline void page_lock(Page* page)
-{
- while (!page_trylock(page)) {
- /* spin — acceptable for short critical sections only */
- }
-}
-
-inline void page_unlock(Page* page)
-{
- page->flags.fetch_and(~kPgLocked);
-}
-
-/* ================================================================== */
-/* Mapping pointer helpers (Linux convention: low bit = anon) */
-/* ================================================================== */
-
-inline bool page_is_anon(const Page* page)
-{
- return (reinterpret_cast<uint64>(page->mapping) & 1ULL) != 0;
-}
-
-inline void* page_mapping_raw(const Page* page)
-{
- return reinterpret_cast<void*>(
- reinterpret_cast<uint64>(page->mapping) & ~1ULL);
-}
-
-inline void page_set_anon_mapping(Page* page, void* anon_vma)
-{
- page->mapping = reinterpret_cast<void*>(
- reinterpret_cast<uint64>(anon_vma) | 1ULL);
-}
-
-inline void page_set_file_mapping(Page* page, void* file_mapping)
-{
- page->mapping = file_mapping;
-}
diff --git a/na/include/lume/page_cache.h b/na/include/lume/page_cache.h
deleted file mode 100644
index abdc535..0000000
--- a/na/include/lume/page_cache.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * page_cache.h — Page Cache interface
- *
- * Provides a stable abstraction for caching physical pages indexed by
- * (mapping, index) pairs. All file-backed fault paths should go
- * through this interface so that upgrading the backing implementation
- * (e.g. LRU eviction, writeback, radix tree) does not require
- * changing any caller.
- *
- * Current implementation: simple open-addressing hash table.
- * Future: radix tree + LRU lists + writeback integration.
- *
- * Terminology:
- * mapping — opaque pointer identifying the backing object
- * (FileMapping*, or any future cache-able object).
- * index — page-granularity offset within the mapping.
- *
- * Reference: docs/specs/linux_abi_mm_plan.md §2.9, §3
- */
-
-#include <lume/types.h>
-
-struct Page;
-
-/*
- * Look up a cached page by (mapping, index).
- * Returns the Page* if found (with refcount incremented), or nullptr.
- * Caller must call page_cache_put() when done.
- */
-Page* page_cache_get(void* mapping, uint64 index);
-
-/*
- * Insert a page into the cache, associated with (mapping, index).
- * The page's refcount is incremented by the cache.
- * If a page already exists at (mapping, index), returns -EEXIST.
- * Returns 0 on success, negative errno on failure.
- */
-int page_cache_insert(void* mapping, uint64 index, Page* page);
-
-/*
- * Release a reference obtained via page_cache_get().
- * This decrements the page's refcount. If the page was removed
- * from the cache and refcount drops to 0, the page is freed.
- */
-void page_cache_put(Page* page);
-
-/*
- * Remove a page from the cache by (mapping, index).
- * Decrements the cache's reference on the page.
- * Returns the removed Page* (caller does NOT own a new ref),
- * or nullptr if not found.
- */
-Page* page_cache_remove(void* mapping, uint64 index);
-
-/*
- * Remove all pages associated with a given mapping.
- * Used during file close / inode eviction / truncate.
- * Each removed page has its cache reference decremented.
- */
-void page_cache_invalidate(void* mapping);
-
-/*
- * Look up a page; if not found, allocate one, insert it, and return it.
- * The returned page has its refcount incremented (caller must put).
- * Returns nullptr on OOM.
- *
- * This is the primary interface for file-backed fault paths:
- * the caller gets a page and then fills it with file data if kPgUptodate
- * is not set.
- */
-Page* page_cache_get_or_alloc(void* mapping, uint64 index);
-
-/*
- * Initialize the page cache subsystem.
- * Called once during boot after slab_init().
- */
-void page_cache_init();
diff --git a/na/include/lume/pgrp.h b/na/include/lume/pgrp.h
deleted file mode 100644
index 127f3f8..0000000
--- a/na/include/lume/pgrp.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Process Group / Session infrastructure.
- *
- * Linux models pgrp/session via struct pid + PIDTYPE_PGID/PIDTYPE_SID hlists.
- * We use a simpler design that supports the same operations cheaply:
- *
- * - Each thread group owns one signal_struct (refcount-shared with
- * CLONE_THREAD siblings).
- * - signal_struct carries pgid/sid and is the linked-list node on a
- * fixed-size pgrp hash table.
- * - kill(pid<=0), tty_signal_pgrp, orphan checks all walk one bucket.
- *
- * Concurrency: g_pgrp_lock protects every bucket and the pgrp_link of
- * every signal_struct on a bucket. signal_struct::lock is a finer-grain
- * lock for fields that change without re-hashing (e.g. tty pointer).
- *
- * Reference: Linux include/linux/pid.h, kernel/pid.c
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <kernel/sync/spinlock.h>
-
-struct signal_struct;
-struct TaskControlBlock;
-
-/* ================================================================== */
-/* Subsystem init */
-/* ================================================================== */
-
-void pgrp_init();
-
-/* ================================================================== */
-/* signal_struct lifecycle */
-/* ================================================================== */
-
-/*
- * Allocate a new signal_struct for `leader`, with sid=pgid=leader_pid,
- * refcount=1, attached to pgrp `leader_pid`. Returns nullptr on OOM.
- */
-signal_struct* alloc_signal_struct(int32 leader_pid,
- TaskControlBlock* leader);
-
-/*
- * Allocate a child signal_struct inheriting pgid/sid/tty from `parent`.
- * The new struct is attached to the parent's pgrp and starts with
- * refcount=1. Used by fork (non-CLONE_THREAD).
- */
-signal_struct* fork_signal_struct(signal_struct* parent,
- TaskControlBlock* child_leader);
-
-/* Increment refcount (CLONE_THREAD share). */
-void get_signal_struct(signal_struct* sig);
-
-/*
- * Decrement refcount; on last drop, detach from pgrp hash and free.
- * Pass the task that's dropping the reference so we can manage
- * leader pointers if the leader exits while sub-threads remain.
- */
-void put_signal_struct(signal_struct* sig);
-
-/* ================================================================== */
-/* Pgrp operations (assume signal_struct is allocated) */
-/* ================================================================== */
-
-/*
- * Move a signal_struct from its current pgrp to `new_pgid`.
- * Used by setpgid / setsid. Takes g_pgrp_lock internally.
- */
-void change_pgid(signal_struct* sig, int32 new_pgid);
-
-/*
- * Invoke `fn(sig)` for every signal_struct whose pgid == target.
- * Holds g_pgrp_lock for the entire walk — fn must not block, sleep,
- * or take locks that order before g_pgrp_lock.
- *
- * Template-y but kept in header to avoid a function-ptr indirection;
- * keep callsites lean.
- */
-template <typename F>
-void for_each_pgrp_member(int32 target_pgid, F&& fn);
-
-/*
- * Count the number of signal_structs in `pgid`. Used by setsid
- * preflight (can't create a session if you're already a pgrp leader
- * with members) and orphan checks.
- */
-int pgrp_member_count(int32 pgid);
-
-/* ================================================================== */
-/* Implementation details exposed for the template above */
-/* ================================================================== */
-
-constexpr int kPgrpHashShift = 6;
-constexpr int kPgrpHashSize = 1 << kPgrpHashShift; /* 64 buckets */
-constexpr int kPgrpHashMask = kPgrpHashSize - 1;
-
-extern list_node g_pgrp_hash[kPgrpHashSize];
-extern Spinlock g_pgrp_lock;
-
-inline int pgrp_hash_idx(int32 pgid) {
- return static_cast<int>(static_cast<uint32>(pgid)) & kPgrpHashMask;
-}
-
-#include <lume/signal_types.h>
-
-template <typename F>
-void for_each_pgrp_member(int32 target_pgid, F&& fn)
-{
- LockGuard guard(g_pgrp_lock);
- list_node* bucket = &g_pgrp_hash[pgrp_hash_idx(target_pgid)];
- list_node* node = bucket->next;
- while (node != bucket) {
- list_node* nxt = node->next; /* fn may not unlink, but be safe */
- auto* sig = list_entry<signal_struct, &signal_struct::pgrp_link>(node);
- if (sig->pgid == target_pgid)
- fn(sig);
- node = nxt;
- }
-}
diff --git a/na/include/lume/pgtable_types.h b/na/include/lume/pgtable_types.h
deleted file mode 100644
index e309a82..0000000
--- a/na/include/lume/pgtable_types.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Page Table Entry Types — strongly typed wrappers.
- *
- * Layout and macro names mirror Linux's arch/riscv/include/asm/page.h
- * and pgtable-64.h so that arch/riscv/mm/init.c can be copied from
- * Linux without source edits. The wrapped struct prevents accidental
- * mixing of a pte_t with a pgd_t or a raw physical address, which the
- * previous bare-uint64 design in pmap.cc could not catch.
- *
- * Underlying width on RV64 LP64 is `unsigned long` = 64 bits, matching
- * Linux exactly — assembly references (e.g. REG_S/REG_L on a pte_t*)
- * see the same byte layout.
- *
- * SV39 uses 3 levels (pgd -> pmd -> pte); SV48 uses 4 (pgd -> pud ->
- * pmd -> pte). p4d_t/pud_t are emitted unconditionally so that the
- * higher-level abstractions in asm-generic/pgtable-nopud.h and friends
- * (which Linux uses to collapse unused levels) work without #ifdef.
- */
-
-#include <lume/types.h>
-
-typedef struct {
- unsigned long pte;
-} pte_t;
-
-typedef struct {
- unsigned long pmd;
-} pmd_t;
-
-typedef struct {
- unsigned long pud;
-} pud_t;
-
-typedef struct {
- unsigned long p4d;
-} p4d_t;
-
-typedef struct {
- unsigned long pgd;
-} pgd_t;
-
-typedef struct {
- unsigned long pgprot;
-} pgprot_t;
-
-typedef struct page *pgtable_t;
-
-/* Field accessors */
-#define pte_val(x) ((x).pte)
-#define pmd_val(x) ((x).pmd)
-#define pud_val(x) ((x).pud)
-#define p4d_val(x) ((x).p4d)
-#define pgd_val(x) ((x).pgd)
-#define pgprot_val(x) ((x).pgprot)
-
-/* Constructors (cast raw uint64 to wrapped type) */
-#define __pte(x) ((pte_t) { (x) })
-#define __pmd(x) ((pmd_t) { (x) })
-#define __pud(x) ((pud_t) { (x) })
-#define __p4d(x) ((p4d_t) { (x) })
-#define __pgd(x) ((pgd_t) { (x) })
-#define __pgprot(x) ((pgprot_t) { (x) })
-
-#define PTE_FMT "%016lx"
diff --git a/na/include/lume/pid.h b/na/include/lume/pid.h
deleted file mode 100644
index 8503750..0000000
--- a/na/include/lume/pid.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * PID Allocator — Bitmap-based O(1) block search
- * Reference: docs/specs/task.md §3.2
- */
-
-#include <lume/types.h>
-#include <kernel/sync/spinlock.h>
-
-inline constexpr int32 kMaxPid = 32768;
-
-class PidAllocator {
-public:
- void init();
- int32 alloc();
- void free(int32 pid);
-
-private:
- static constexpr int32 kBitmapWords = kMaxPid / 64;
- Spinlock lock_;
- uint64 bitmap_[kBitmapWords];
- int32 next_hint_ = 2;
-};
-
-extern PidAllocator g_pid_alloc;
diff --git a/na/include/lume/pipe.h b/na/include/lume/pipe.h
deleted file mode 100644
index c905c9a..0000000
--- a/na/include/lume/pipe.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * pipe.h — Pipe data structure and API
- *
- * A pipe is a unidirectional byte stream backed by a fixed-size
- * circular buffer. Two file structs (reader + writer) share a
- * single pipe_inode_info.
- */
-
-#include <lume/types.h>
-#include <lume/atomic.h>
-#include <lume/waitqueue.h>
-#include <kernel/sync/spinlock.h>
-
-struct file;
-struct files_struct;
-
-/* ================================================================== */
-/* pipe_inode_info */
-/* ================================================================== */
-
-inline constexpr int PIPE_BUF_SIZE = 4096;
-
-struct pipe_inode_info {
- char buf[PIPE_BUF_SIZE];
- int rd_pos; /* Read position in circular buffer */
- int wr_pos; /* Write position in circular buffer */
- int len; /* Bytes currently in buffer */
- Spinlock lock;
- WaitQueue rd_wait; /* Readers sleep here when empty */
- WaitQueue wr_wait; /* Writers sleep here when full */
-
- lume::atomic<int32> readers; /* Number of open read ends */
- lume::atomic<int32> writers; /* Number of open write ends */
- lume::atomic<int32> refcount; /* Total references */
-};
-
-/* ================================================================== */
-/* API */
-/* ================================================================== */
-
-/* Create a pipe, install read/write fds into the given files_struct.
- * Returns 0 on success, negative errno on failure. */
-int do_pipe2(files_struct* files, int fds[2], int flags);
-
-/* Allocate a fresh pipe_inode_info */
-pipe_inode_info* alloc_pipe_info();
-void free_pipe_info(pipe_inode_info* pipe);
diff --git a/na/include/lume/platform_device.h b/na/include/lume/platform_device.h
deleted file mode 100644
index ac3e527..0000000
--- a/na/include/lume/platform_device.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * platform_device / platform_driver — FDT-described memory-mapped devices.
- *
- * Mirrors Linux include/linux/platform_device.h. The platform bus is
- * the catch-all bus for devices that don't live on a real bus like PCI
- * or USB — every device discovered via the FDT lands here.
- *
- * Reference: docs/specs/storage_stack.md §4.3
- */
-
-#include <lume/types.h>
-#include <lume/device.h>
-
-class DeviceNode;
-
-/* Compatible-string -> driver-private data table (Linux's of_device_id). */
-struct of_device_id {
- const char* compatible;
- const void* data;
-};
-
-struct platform_device {
- device dev;
- DeviceNode* of_node; // FDT node this device came from
- uint64 mmio_base; // first `reg` cell from FDT (0 if none)
- uint64 mmio_size; // second `reg` cell (0 if none)
- int irq; // first interrupt (-1 if none / not parsed)
-};
-
-struct platform_driver {
- device_driver driver;
- int (*probe)(platform_device*);
- void (*remove)(platform_device*);
- const of_device_id* of_match_table; // NULL-terminated array
-};
-
-extern bus_type platform_bus;
-
-void platform_bus_init();
-int platform_driver_register(platform_driver* drv);
-int platform_device_register(platform_device* pdev);
-
-/* Helper: pull current driver out of a probed device. */
-inline platform_driver* to_platform_driver(device_driver* drv)
-{
- return container_of(drv, platform_driver, driver);
-}
-inline platform_device* to_platform_device(device* dev)
-{
- return container_of(dev, platform_device, dev);
-}
-
-/* Linker-set registration macro — drop a pointer to your platform_driver
- * into the .drivers section. driver_probe_all() iterates this set at
- * boot. Use a unique token in CONCAT to avoid name collisions. */
-#define PLATFORM_DRIVER(_drv) \
- extern platform_driver _drv; \
- static platform_driver* const __pdrv_ptr_##_drv \
- __attribute__((used, section(".drivers"))) = &_drv
diff --git a/na/include/lume/pmm.h b/na/include/lume/pmm.h
deleted file mode 100644
index 3e4b819..0000000
--- a/na/include/lume/pmm.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Physical Memory Manager (PMM) — Buddy System + Per-CPU Cache
- *
- * Manages all physical pages discovered via FDT. Built on a
- * power-of-two buddy allocator for multi-page requests, with a
- * Per-CPU single-page cache (PCP) to reduce lock contention on the
- * hot single-page alloc/free path.
- *
- * Concurrency model:
- * - PCP operations run with interrupts disabled (same-CPU safety).
- * - Global buddy operations are protected by a single Spinlock.
- * - Reference counts use __atomic builtins (lock-free).
- *
- * Reference: docs/specs/pmm.md
- */
-
-#include <lume/types.h>
-#include <lume/page.h>
-#include <lume/addr_types.h>
-#include <lume/config.h>
-
-/* Buddy system constants */
-inline constexpr int kMaxOrder = CONFIG_MAX_ORDER; // Orders 0..(N-1)
-
-/* Per-CPU cache dynamic parameters (calculated in pmm_init) */
-extern uint32 g_pcp_high_watermark;
-extern uint32 g_pcp_batch_size;
-
-/* ================================================================== */
-/* Public free-function API */
-/* ================================================================== */
-
-/*
- * Initialize the PMM. Called by BSP after vmm_init() + memblock.
- * page_map is already mapped at VMEMMAP VA by vmm_init.
- * Populates buddy lists from memblock's free regions, then retires memblock.
- * Panics on failure.
- */
-void pmm_init();
-
-/* --- Single-page fast path (uses PCP) --- */
-
-// Allocate one 4KB page. Returns nullptr on OOM.
-Page* pmm_alloc_page();
-
-// Free one 4KB page.
-void pmm_free_page(Page* page);
-
-/* --- Multi-page slow path (direct buddy) --- */
-
-// Allocate 2^order contiguous pages. Returns nullptr on OOM.
-Page* pmm_alloc_pages(uint8 order);
-
-// Free 2^order contiguous pages.
-void pmm_free_pages(Page* page, uint8 order);
-
-/* --- Reference counting (atomic, lock-free) --- */
-
-void page_incref(Page* page);
-void page_decref(Page* page);
-
-/* --- Map counting (atomic, lock-free) --- */
-
-inline void page_inc_mapcount(Page* page) {
- page->mapcount.fetch_add(1);
-}
-
-inline uint32 page_dec_mapcount(Page* page) {
- return page->mapcount.fetch_sub(1) - 1; // Returns new value
-}
-
-inline uint32 page_mapcount(const Page* page) {
- return page->mapcount.load(__ATOMIC_RELAXED);
-}
-
-/* --- Address conversion --- */
-
-// Convert a Page* to its physical address.
-PhysAddr page_to_pa(const Page* page);
-
-// Convert a physical address to its Page*.
-Page* pa_to_page(PhysAddr pa);
-
-// Convert a Page* to a kernel virtual address.
-VirtAddr page_to_va(const Page* page);
-
-/* --- PFN (Page Frame Number) abstraction --- */
-/*
- * PFN is the fundamental unit for PA↔Page conversion.
- * PA↔PFN converters already exist in addr.h (pa_to_pfn / pfn_to_pa).
- * When sparse vmemmap lands, only pfn_to_page/page_to_pfn
- * need to change — no callers are affected.
- */
-
-// Convert PFN to Page*. PFN = pa / PAGE_SIZE.
-Page* pfn_to_page(uint64 pfn);
-
-// Convert Page* to PFN.
-uint64 page_to_pfn(const Page* page);
-
-// Query PMM memory range (for VMM kernel mapping)
-PhysAddr pmm_get_mem_base();
-uint64 pmm_get_mem_size();
diff --git a/na/include/lume/poll.h b/na/include/lume/poll.h
deleted file mode 100644
index 7d5bca2..0000000
--- a/na/include/lume/poll.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * poll.h — POSIX poll() / ppoll() infrastructure.
- *
- * Linux pattern (include/linux/poll.h):
- * - file_operations->poll(file, poll_table) returns a bitmask of
- * POLL* events that are currently true on this file.
- * - As a side effect, the poll callback calls poll_wait(file, wq, pt)
- * to register the caller on every wait queue that might fire when
- * state changes. sys_ppoll() then sleeps on those queues until
- * one of them fires or a signal/timeout arrives.
- *
- * Our minimal implementation supports a SINGLE wait queue per poll call
- * (sufficient for nfds==1, which is the BusyBox interactive shell case).
- * Multi-fd polls work but fall back to busy re-checking; a proper
- * per-fd waiter-list upgrade can come later.
- */
-
-#include <lume/types.h>
-
-struct file;
-class WaitQueue;
-
-/* ================================================================== */
-/* UAPI: pollfd struct + POLL* event bits (Linux asm-generic) */
-/* ================================================================== */
-
-struct pollfd {
- int32 fd;
- int16 events;
- int16 revents;
-};
-
-inline constexpr int16 POLLIN = 0x0001;
-inline constexpr int16 POLLPRI = 0x0002;
-inline constexpr int16 POLLOUT = 0x0004;
-inline constexpr int16 POLLERR = 0x0008;
-inline constexpr int16 POLLHUP = 0x0010;
-inline constexpr int16 POLLNVAL = 0x0020;
-inline constexpr int16 POLLRDNORM = 0x0040;
-inline constexpr int16 POLLRDBAND = 0x0080;
-inline constexpr int16 POLLWRNORM = 0x0100;
-inline constexpr int16 POLLWRBAND = 0x0200;
-
-/* ================================================================== */
-/* Kernel-side poll_table */
-/* ================================================================== */
-
-/*
- * Carried into file_operations->poll(). Drivers call poll_wait() with
- * the wait queue they'd like the poller to sleep on; we record the
- * first one. When `wq == nullptr` on entry, no real registration is
- * needed (the caller is just probing readiness).
- */
-struct poll_table {
- WaitQueue* wq; /* first wait queue registered (single-wq design) */
-};
-
-/*
- * Called from f_op->poll() implementations to register a wait queue.
- * If `pt` is null we're in a probe-only pass — caller doesn't want to
- * sleep, just to read current revents.
- */
-void poll_wait(file* fp, WaitQueue* wq, poll_table* pt);
diff --git a/na/include/lume/ptrace.h b/na/include/lume/ptrace.h
deleted file mode 100644
index 687d103..0000000
--- a/na/include/lume/ptrace.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * PtRegs — Linux-compatible user register context
- *
- * This structure MUST match the Linux RISC-V pt_regs layout exactly.
- * It is the canonical representation for:
- * - Signal frame construction (ucontext / sigcontext)
- * - ptrace(GETREGS/SETREGS)
- * - /proc/<pid>/regs
- *
- * The internal TrapFrame (arch/trapframe.h) has a DIFFERENT layout
- * optimized for fast trap entry/exit. Conversion between the two
- * is done via trapframe_to_pt_regs() / pt_regs_to_trapframe().
- *
- * Reference: Linux arch/riscv/include/asm/ptrace.h
- */
-
-#include <lume/types.h>
-
-struct PtRegs {
- uint64 epc; // Program counter at trap
- uint64 ra; // x1
- uint64 sp; // x2
- uint64 gp; // x3
- uint64 tp; // x4
- uint64 t0; // x5
- uint64 t1; // x6
- uint64 t2; // x7
- uint64 s0; // x8
- uint64 s1; // x9
- uint64 a0; // x10
- uint64 a1; // x11
- uint64 a2; // x12
- uint64 a3; // x13
- uint64 a4; // x14
- uint64 a5; // x15
- uint64 a6; // x16
- uint64 a7; // x17
- uint64 s2; // x18
- uint64 s3; // x19
- uint64 s4; // x20
- uint64 s5; // x21
- uint64 s6; // x22
- uint64 s7; // x23
- uint64 s8; // x24
- uint64 s9; // x25
- uint64 s10; // x26
- uint64 s11; // x27
- uint64 t3; // x28
- uint64 t4; // x29
- uint64 t5; // x30
- uint64 t6; // x31
- /* CSRs */
- uint64 status; // sstatus
- uint64 badaddr; // stval
- uint64 cause; // scause
- /* Syscall bookkeeping */
- uint64 orig_a0; // a0 value before syscall (for restart)
-};
-
-static_assert(sizeof(PtRegs) == 36 * 8, "PtRegs must be 288 bytes (Linux ABI)");
-
-/*
- * Convert between internal TrapFrame and Linux-ABI PtRegs.
- * These are the ONLY places that know both layouts.
- * Future TrapFrame restructuring only needs to update these two functions.
- */
-namespace arch { struct TrapFrame; }
-
-void trapframe_to_pt_regs(PtRegs* dst, const arch::TrapFrame* src,
- uint64 scause, uint64 stval);
-void pt_regs_to_trapframe(arch::TrapFrame* dst, const PtRegs* src);
diff --git a/na/include/lume/random.h b/na/include/lume/random.h
deleted file mode 100644
index 740b060..0000000
--- a/na/include/lume/random.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-#include <lume/types.h>
-
-enum class RandomSeedSource : uint8 {
- Unknown = 0,
- FdtProvided,
- HwRandom,
- CycleFallback,
-};
-
-/*
- * MI: Machine-Independent Random Number Generator API
- *
- * Provides early PRNG and eventually a full CSPRNG.
- */
-
-/*
- * Initialize early PRNG with a seed (e.g., from FDT).
- * If seed is 0, attempts to use hardware TRNG or falls back to timer jitter.
- */
-void random_early_init(uint64 seed);
-
-/* Consume random bits */
-uint64 get_random_u64();
-uint32 get_random_u32();
-void get_random_bytes(void* buf, size_t len);
-
-/* Inject entropy (for future interrupt/device mixing) */
-void add_device_randomness(const void* buf, size_t len);
-
-/* Early-seed metadata (for boot diagnostics/KASLR logs). */
-RandomSeedSource random_early_seed_source();
-uint64 random_early_seed_value();
-const char* random_seed_source_name(RandomSeedSource source);
diff --git a/na/include/lume/rbtree.h b/na/include/lume/rbtree.h
deleted file mode 100644
index 80b0c6c..0000000
--- a/na/include/lume/rbtree.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Red-Black Trees — public API
- * Adapted from Linux kernel include/linux/rbtree.h
- *
- * (C) 1999 Andrea Arcangeli <andrea@suse.de>
- */
-
-#pragma once
-
-#include <lume/compiler.h>
-#include <lume/rbtree_types.h>
-#include <lume/container_of.h>
-
-/* ------------------------------------------------------------------ */
-/* Core macros */
-/* ------------------------------------------------------------------ */
-
-#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3UL))
-
-#define rb_entry(ptr, type, member) container_of(ptr, type, member)
-
-#define RB_EMPTY_ROOT(root) (READ_ONCE((root)->rb_node) == nullptr)
-
-#define RB_EMPTY_NODE(node) \
- ((node)->__rb_parent_color == (unsigned long)(node))
-#define RB_CLEAR_NODE(node) \
- ((node)->__rb_parent_color = (unsigned long)(node))
-
-/* ------------------------------------------------------------------ */
-/* Core API (implemented in lib/rbtree.cc) */
-/* ------------------------------------------------------------------ */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void rb_insert_color(struct rb_node *, struct rb_root *);
-void rb_erase(struct rb_node *, struct rb_root *);
-
-struct rb_node *rb_next(const struct rb_node *);
-struct rb_node *rb_prev(const struct rb_node *);
-struct rb_node *rb_first(const struct rb_root *);
-struct rb_node *rb_last(const struct rb_root *);
-
-struct rb_node *rb_first_postorder(const struct rb_root *);
-struct rb_node *rb_next_postorder(const struct rb_node *);
-
-void rb_replace_node(struct rb_node *victim, struct rb_node *_new,
- struct rb_root *root);
-
-#ifdef __cplusplus
-}
-#endif
-
-/* ------------------------------------------------------------------ */
-/* Inline helpers */
-/* ------------------------------------------------------------------ */
-
-static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,
- struct rb_node **rb_link)
-{
- node->__rb_parent_color = (unsigned long)parent;
- node->rb_left = node->rb_right = nullptr;
- *rb_link = node;
-}
-
-#define rb_entry_safe(ptr, type, member) \
- ({ __typeof__(ptr) ____ptr = (ptr); \
- ____ptr ? rb_entry(____ptr, type, member) : nullptr; \
- })
-
-/* ------------------------------------------------------------------ */
-/* Cached (leftmost) tree helpers */
-/* ------------------------------------------------------------------ */
-
-#define rb_first_cached(root) (root)->rb_leftmost
-
-static inline void rb_insert_color_cached(struct rb_node *node,
- struct rb_root_cached *root,
- bool leftmost)
-{
- if (leftmost)
- root->rb_leftmost = node;
- rb_insert_color(node, &root->rb_root);
-}
-
-static inline void rb_erase_cached(struct rb_node *node,
- struct rb_root_cached *root)
-{
- if (root->rb_leftmost == node)
- root->rb_leftmost = rb_next(node);
- rb_erase(node, &root->rb_root);
-}
-
-static inline void rb_replace_node_cached(struct rb_node *victim,
- struct rb_node *_new,
- struct rb_root_cached *root)
-{
- if (root->rb_leftmost == victim)
- root->rb_leftmost = _new;
- rb_replace_node(victim, _new, &root->rb_root);
-}
-
-static inline void rb_erase_init(struct rb_node *n, struct rb_root *root)
-{
- rb_erase(n, root);
- RB_CLEAR_NODE(n);
-}
-
-/* ------------------------------------------------------------------ */
-/* Generic insert / find helpers (inline, using function pointers) */
-/* ------------------------------------------------------------------ */
-
-static inline void
-rb_add(struct rb_node *node, struct rb_root *tree,
- bool (*less)(struct rb_node *, const struct rb_node *))
-{
- struct rb_node **link = &tree->rb_node;
- struct rb_node *parent = nullptr;
-
- while (*link) {
- parent = *link;
- if (less(node, parent))
- link = &parent->rb_left;
- else
- link = &parent->rb_right;
- }
-
- rb_link_node(node, parent, link);
- rb_insert_color(node, tree);
-}
-
-static inline void
-rb_add_cached(struct rb_node *node, struct rb_root_cached *tree,
- bool (*less)(struct rb_node *, const struct rb_node *))
-{
- struct rb_node **link = &tree->rb_root.rb_node;
- struct rb_node *parent = nullptr;
- bool leftmost = true;
-
- while (*link) {
- parent = *link;
- if (less(node, parent)) {
- link = &parent->rb_left;
- } else {
- link = &parent->rb_right;
- leftmost = false;
- }
- }
-
- rb_link_node(node, parent, link);
- rb_insert_color_cached(node, tree, leftmost);
-}
-
-static inline struct rb_node *
-rb_find(const void *key, const struct rb_root *tree,
- int (*cmp)(const void *key, const struct rb_node *))
-{
- struct rb_node *node = tree->rb_node;
-
- while (node) {
- int c = cmp(key, node);
- if (c < 0)
- node = node->rb_left;
- else if (c > 0)
- node = node->rb_right;
- else
- return node;
- }
- return nullptr;
-}
-
-static inline struct rb_node *
-rb_find_add(struct rb_node *node, struct rb_root *tree,
- int (*cmp)(struct rb_node *, const struct rb_node *))
-{
- struct rb_node **link = &tree->rb_node;
- struct rb_node *parent = nullptr;
- int c;
-
- while (*link) {
- parent = *link;
- c = cmp(node, parent);
- if (c < 0)
- link = &parent->rb_left;
- else if (c > 0)
- link = &parent->rb_right;
- else
- return parent;
- }
-
- rb_link_node(node, parent, link);
- rb_insert_color(node, tree);
- return nullptr;
-}
-
-/* ------------------------------------------------------------------ */
-/* Iteration macros */
-/* ------------------------------------------------------------------ */
-
-#define rbtree_postorder_for_each_entry_safe(pos, n, root, field) \
- for (pos = rb_entry_safe(rb_first_postorder(root), __typeof__(*pos), field); \
- pos && ({ n = rb_entry_safe(rb_next_postorder(&pos->field), \
- __typeof__(*pos), field); 1; }); \
- pos = n)
-
diff --git a/na/include/lume/rbtree_augmented.h b/na/include/lume/rbtree_augmented.h
deleted file mode 100644
index d4559c4..0000000
--- a/na/include/lume/rbtree_augmented.h
+++ /dev/null
@@ -1,252 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Red-Black Trees — augmented operations & internal helpers
- * Adapted from Linux kernel include/linux/rbtree_augmented.h
- *
- * (C) 1999 Andrea Arcangeli <andrea@suse.de>
- * (C) 2002 David Woodhouse <dwmw2@infradead.org>
- * (C) 2012 Michel Lespinasse <walken@google.com>
- */
-
-#pragma once
-
-#include <lume/rbtree.h>
-
-/* ------------------------------------------------------------------ */
-/* Augmented callback structure */
-/* ------------------------------------------------------------------ */
-
-struct rb_augment_callbacks {
- void (*propagate)(struct rb_node *node, struct rb_node *stop);
- void (*copy)(struct rb_node *old, struct rb_node *_new);
- void (*rotate)(struct rb_node *old, struct rb_node *_new);
-};
-
-extern "C" void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,
- void (*augment_rotate)(struct rb_node *old, struct rb_node *_new));
-
-extern "C" void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
- void (*augment_rotate)(struct rb_node *old, struct rb_node *_new));
-
-static inline void
-rb_insert_augmented(struct rb_node *node, struct rb_root *root,
- const struct rb_augment_callbacks *augment)
-{
- __rb_insert_augmented(node, root, augment->rotate);
-}
-
-static inline void
-rb_insert_augmented_cached(struct rb_node *node,
- struct rb_root_cached *root, bool newleft,
- const struct rb_augment_callbacks *augment)
-{
- if (newleft)
- root->rb_leftmost = node;
- rb_insert_augmented(node, &root->rb_root, augment);
-}
-
-/* ------------------------------------------------------------------ */
-/* Color / parent encoding helpers */
-/* ------------------------------------------------------------------ */
-
-#define RB_RED 0
-#define RB_BLACK 1
-
-#define __rb_parent(pc) ((struct rb_node *)(pc & ~3UL))
-
-#define __rb_color(pc) ((pc) & 1)
-#define __rb_is_black(pc) __rb_color(pc)
-#define __rb_is_red(pc) (!__rb_color(pc))
-#define rb_color(rb) __rb_color((rb)->__rb_parent_color)
-#define rb_is_red(rb) __rb_is_red((rb)->__rb_parent_color)
-#define rb_is_black(rb) __rb_is_black((rb)->__rb_parent_color)
-
-static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
-{
- rb->__rb_parent_color = rb_color(rb) + (unsigned long)p;
-}
-
-static inline void rb_set_parent_color(struct rb_node *rb,
- struct rb_node *p, int color)
-{
- rb->__rb_parent_color = (unsigned long)p + color;
-}
-
-static inline void
-__rb_change_child(struct rb_node *old, struct rb_node *_new,
- struct rb_node *parent, struct rb_root *root)
-{
- if (parent) {
- if (parent->rb_left == old)
- WRITE_ONCE(parent->rb_left, _new);
- else
- WRITE_ONCE(parent->rb_right, _new);
- } else
- WRITE_ONCE(root->rb_node, _new);
-}
-
-/* ------------------------------------------------------------------ */
-/* Augmented erase (inline) */
-/* ------------------------------------------------------------------ */
-
-static __always_inline struct rb_node *
-__rb_erase_augmented(struct rb_node *node, struct rb_root *root,
- const struct rb_augment_callbacks *augment)
-{
- struct rb_node *child = node->rb_right;
- struct rb_node *tmp = node->rb_left;
- struct rb_node *parent, *rebalance;
- unsigned long pc;
-
- if (!tmp) {
- /*
- * Case 1: node to erase has no more than 1 child (easy!)
- */
- pc = node->__rb_parent_color;
- parent = __rb_parent(pc);
- __rb_change_child(node, child, parent, root);
- if (child) {
- child->__rb_parent_color = pc;
- rebalance = nullptr;
- } else
- rebalance = __rb_is_black(pc) ? parent : nullptr;
- tmp = parent;
- } else if (!child) {
- /* Still case 1, but this time the child is node->rb_left */
- tmp->__rb_parent_color = pc = node->__rb_parent_color;
- parent = __rb_parent(pc);
- __rb_change_child(node, tmp, parent, root);
- rebalance = nullptr;
- tmp = parent;
- } else {
- struct rb_node *successor = child, *child2;
-
- tmp = child->rb_left;
- if (!tmp) {
- /*
- * Case 2: node's successor is its right child
- */
- parent = successor;
- child2 = successor->rb_right;
-
- augment->copy(node, successor);
- } else {
- /*
- * Case 3: node's successor is leftmost under
- * node's right child subtree
- */
- do {
- parent = successor;
- successor = tmp;
- tmp = tmp->rb_left;
- } while (tmp);
- child2 = successor->rb_right;
- WRITE_ONCE(parent->rb_left, child2);
- WRITE_ONCE(successor->rb_right, child);
- rb_set_parent(child, successor);
-
- augment->copy(node, successor);
- augment->propagate(parent, successor);
- }
-
- tmp = node->rb_left;
- WRITE_ONCE(successor->rb_left, tmp);
- rb_set_parent(tmp, successor);
-
- pc = node->__rb_parent_color;
- tmp = __rb_parent(pc);
- __rb_change_child(node, successor, tmp, root);
-
- if (child2) {
- rb_set_parent_color(child2, parent, RB_BLACK);
- rebalance = nullptr;
- } else {
- rebalance = rb_is_black(successor) ? parent : nullptr;
- }
- successor->__rb_parent_color = pc;
- tmp = successor;
- }
-
- augment->propagate(tmp, nullptr);
- return rebalance;
-}
-
-static __always_inline void
-rb_erase_augmented(struct rb_node *node, struct rb_root *root,
- const struct rb_augment_callbacks *augment)
-{
- struct rb_node *rebalance = __rb_erase_augmented(node, root, augment);
- if (rebalance)
- __rb_erase_color(rebalance, root, augment->rotate);
-}
-
-static __always_inline void
-rb_erase_augmented_cached(struct rb_node *node, struct rb_root_cached *root,
- const struct rb_augment_callbacks *augment)
-{
- if (root->rb_leftmost == node)
- root->rb_leftmost = rb_next(node);
- rb_erase_augmented(node, &root->rb_root, augment);
-}
-
-/* ------------------------------------------------------------------ */
-/* Augmented callback declaration macros */
-/* ------------------------------------------------------------------ */
-
-#define RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME, \
- RBSTRUCT, RBFIELD, RBAUGMENTED, RBCOMPUTE) \
-static inline void \
-RBNAME ## _propagate(struct rb_node *rb, struct rb_node *stop) \
-{ \
- while (rb != stop) { \
- RBSTRUCT *node = rb_entry(rb, RBSTRUCT, RBFIELD); \
- if (RBCOMPUTE(node, true)) \
- break; \
- rb = rb_parent(&node->RBFIELD); \
- } \
-} \
-static inline void \
-RBNAME ## _copy(struct rb_node *rb_old, struct rb_node *rb_new) \
-{ \
- RBSTRUCT *old_ = rb_entry(rb_old, RBSTRUCT, RBFIELD); \
- RBSTRUCT *new_ = rb_entry(rb_new, RBSTRUCT, RBFIELD); \
- new_->RBAUGMENTED = old_->RBAUGMENTED; \
-} \
-static void \
-RBNAME ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new) \
-{ \
- RBSTRUCT *old_ = rb_entry(rb_old, RBSTRUCT, RBFIELD); \
- RBSTRUCT *new_ = rb_entry(rb_new, RBSTRUCT, RBFIELD); \
- new_->RBAUGMENTED = old_->RBAUGMENTED; \
- RBCOMPUTE(old_, false); \
-} \
-RBSTATIC const struct rb_augment_callbacks RBNAME = { \
- RBNAME ## _propagate, \
- RBNAME ## _copy, \
- RBNAME ## _rotate \
-};
-
-#define RB_DECLARE_CALLBACKS_MAX(RBSTATIC, RBNAME, RBSTRUCT, RBFIELD, \
- RBTYPE, RBAUGMENTED, RBCOMPUTE) \
-static inline bool RBNAME ## _compute_max(RBSTRUCT *node, bool exit_early) \
-{ \
- RBSTRUCT *child; \
- RBTYPE max = RBCOMPUTE(node); \
- if (node->RBFIELD.rb_left) { \
- child = rb_entry(node->RBFIELD.rb_left, RBSTRUCT, RBFIELD); \
- if (child->RBAUGMENTED > max) \
- max = child->RBAUGMENTED; \
- } \
- if (node->RBFIELD.rb_right) { \
- child = rb_entry(node->RBFIELD.rb_right, RBSTRUCT, RBFIELD); \
- if (child->RBAUGMENTED > max) \
- max = child->RBAUGMENTED; \
- } \
- if (exit_early && node->RBAUGMENTED == max) \
- return true; \
- node->RBAUGMENTED = max; \
- return false; \
-} \
-RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME, \
- RBSTRUCT, RBFIELD, RBAUGMENTED, RBNAME ## _compute_max)
-
diff --git a/na/include/lume/rbtree_types.h b/na/include/lume/rbtree_types.h
deleted file mode 100644
index 688ade4..0000000
--- a/na/include/lume/rbtree_types.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Red-Black Tree type definitions
- * Adapted from Linux kernel include/linux/rbtree_types.h
- */
-
-#pragma once
-
-#include <lume/types.h>
-
-struct rb_node {
- unsigned long __rb_parent_color;
- struct rb_node *rb_right;
- struct rb_node *rb_left;
-} __attribute__((aligned(sizeof(long))));
-
-struct rb_root {
- struct rb_node *rb_node;
-};
-
-struct rb_root_cached {
- struct rb_root rb_root;
- struct rb_node *rb_leftmost;
-};
-
-#define RB_ROOT (struct rb_root) { nullptr, }
-#define RB_ROOT_CACHED (struct rb_root_cached) { {nullptr, }, nullptr }
-
diff --git a/na/include/lume/sched.h b/na/include/lume/sched.h
deleted file mode 100644
index ab26bac..0000000
--- a/na/include/lume/sched.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Scheduler Public Interface
- *
- * Linux-style sched_class core with per-CPU runqueues.
- * Phase 2: O(1) bitmap class is the default scheduling class.
- *
- * Reference: docs/specs/scheduler.md
- */
-
-#include <lume/types.h>
-#include <lume/config.h>
-
-struct TaskControlBlock;
-
-/* ========================================================================
- * Scheduler Constants — driven by CONFIG_xxx (see scripts/Kconfig.mk)
- * ======================================================================== */
-
-inline constexpr int kNumPriorities = CONFIG_NR_PRIORITIES;
-inline constexpr int kDefaultPriority = CONFIG_DEFAULT_PRIORITY;
-inline constexpr int kDefaultTimeSlice = CONFIG_DEFAULT_TIMESLICE;
-inline constexpr int kIdlePriority = CONFIG_IDLE_PRIORITY;
-
-/* ========================================================================
- * Initialization
- * ======================================================================== */
-
-/* BSP: initialize all RunQueues, create BSP idle thread and boot TCB. */
-void sched_init();
-
-/* AP: create per-CPU idle thread and boot TCB. */
-void sched_init_ap();
-
-/* ========================================================================
- * Core Scheduling
- * ======================================================================== */
-
-/* Main scheduling entry point. Picks the next task and context-switches.
- * Called voluntarily (yield, sleep, exit) or from trap return path. */
-void schedule();
-
-/* Timer tick handler. Delegates to current task's sched_class.
- * Called from MI trap layer in interrupt context. */
-void sched_tick();
-
-/* Check if rescheduling is needed (called before returning to user mode). */
-void sched_check_preempt();
-
-/* Voluntarily yield the current time slice. */
-void sched_yield();
-
-/* ========================================================================
- * Task Queue Management (used by task.cc, waitqueue.cc)
- * ======================================================================== */
-
-/* Make a task runnable: add it to its CPU's RunQueue. */
-void sched_enqueue(TaskControlBlock* task);
-
-/* Mark a task as sleeping: remove it from the RunQueue. */
-void sched_dequeue(TaskControlBlock* task);
-
-/* ========================================================================
- * Time Slice Policy
- * ======================================================================== */
-
-/* Convert priority level to time slice length.
- * Higher priority (lower number) → longer time slice. */
-int32 priority_to_timeslice(uint8 priority);
-
-/* ========================================================================
- * Task Migration & Load Balancing
- * ======================================================================== */
-
-/*
- * sched_migrate_task — Move a task from its current CPU to target_cpu.
- *
- * The task must NOT be the currently running task. It is dequeued from
- * its old runqueue and enqueued onto the target. Both runqueue locks
- * are acquired in cpu_id order to prevent deadlock.
- *
- * Returns 0 on success, -EINVAL if task is current or target is invalid.
- */
-int sched_migrate_task(TaskControlBlock* task, uint8 target_cpu);
-
-/*
- * sched_balance — Periodic load balancer.
- *
- * Called from sched_tick(). Checks if any CPU has significantly more
- * runnable tasks and pulls work to the local runqueue. This is a
- * simplified version of Linux's load_balance().
- */
-void sched_balance();
-
-/*
- * sched_nr_running — Get the number of runnable tasks on a CPU.
- */
-uint32 sched_nr_running(uint32 cpu_id);
diff --git a/na/include/lume/sched_class.h b/na/include/lume/sched_class.h
deleted file mode 100644
index 08686c1..0000000
--- a/na/include/lume/sched_class.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Scheduler Class Interface (Linux-style)
- *
- * Each scheduling algorithm implements a sched_class and is chained
- * in priority order. The scheduler core walks the chain to select
- * the next runnable task.
- */
-
-#include <lume/types.h>
-
-struct RunQueue;
-struct TaskControlBlock;
-
-struct SchedClass {
- const char* name;
- const SchedClass* next; // Linked list order: higher priority first
-
- void (*init_rq)(RunQueue* rq);
-
- void (*enqueue_task)(RunQueue* rq, TaskControlBlock* task);
- void (*dequeue_task)(RunQueue* rq, TaskControlBlock* task);
- TaskControlBlock* (*pick_next_task)(RunQueue* rq);
- void (*put_prev_task)(RunQueue* rq, TaskControlBlock* task);
-
- void (*task_tick)(RunQueue* rq, TaskControlBlock* curr);
- void (*yield_task)(RunQueue* rq, TaskControlBlock* curr);
-};
-
-extern const SchedClass g_sched_class_o1;
-extern const SchedClass g_sched_class_idle;
diff --git a/na/include/lume/selftest.h b/na/include/lume/selftest.h
deleted file mode 100644
index 7040986..0000000
--- a/na/include/lume/selftest.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Boot Self-Test Framework
- *
- * Minimal assertion-based test harness that runs during kernel_main()
- * after all subsystems are initialized. Each test is a void function
- * that calls ST_ASSERT / ST_ASSERT_EQ macros. On failure, prints
- * the failing expression and panics.
- *
- * Usage:
- * void selftest_pmm(); // defined in test/test_pmm.cc
- * void selftest_slab(); // defined in test/test_slab.cc
- *
- * All self-tests run only on BSP in single-core mode.
- */
-
-#include <lume/types.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-
-#define ST_ASSERT(cond) \
- do { \
- if (!(cond)) { \
- kprintf("[FAIL] %s:%d: assertion failed: %s\n", \
- __FILE__, __LINE__, #cond); \
- kernel_panic("selftest failed", #cond); \
- } \
- } while (0)
-
-#define ST_ASSERT_EQ(a, b) \
- do { \
- auto _a = (a); auto _b = (b); \
- if (_a != _b) { \
- kprintf("[FAIL] %s:%d: %s != %s (0x%llx != 0x%llx)\n", \
- __FILE__, __LINE__, #a, #b, \
- (uint64)(uintptr)(_a), (uint64)(uintptr)(_b)); \
- kernel_panic("selftest failed", #a " != " #b); \
- } \
- } while (0)
-
-#define ST_ASSERT_NE(a, b) \
- do { \
- auto _a = (a); auto _b = (b); \
- if (_a == _b) { \
- kprintf("[FAIL] %s:%d: %s == %s (both 0x%llx)\n", \
- __FILE__, __LINE__, #a, #b, \
- (uint64)(uintptr)(_a)); \
- kernel_panic("selftest failed", #a " == " #b); \
- } \
- } while (0)
-
-/* Announce a test suite beginning/end */
-inline void st_begin(const char* name)
-{
- kprintf("[test] %s ... ", name);
-}
-
-inline void st_pass()
-{
- kprintf("PASS\n");
-}
diff --git a/na/include/lume/setup.h b/na/include/lume/setup.h
deleted file mode 100644
index f8e6cb6..0000000
--- a/na/include/lume/setup.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _LUME_SETUP_H
-#define _LUME_SETUP_H
-
-/*
- * Boot-time interfaces called between assembly and C.
- *
- * Mirrors the subset of Linux include/linux/setup.h relevant to LumeOS's
- * Linux-aligned RISC-V boot flow:
- *
- * setup_vm Build early_pg_dir / trampoline_pg_dir before
- * the MMU is enabled. Filled kernel_map for
- * all subsequent code to consult.
- * relocate_enable_mmu Switch SATP, jump to higher-half, apply
- * .rela.dyn via relocate_kernel.
- * relocate_kernel Walk .rela.dyn, patch R_RISCV_RELATIVE
- * slots with addend + kaslr_offset.
- *
- * kernel_main (LumeOS's equivalent of Linux's start_kernel) is declared
- * in kernel/main.cc and re-declared in arch-side glue — kept local to
- * that file to avoid creating an artificial public API for the lone
- * call site in head.S.
- */
-
-#include <lume/types.h>
-#include <asm/pgtable.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void setup_vm(uintptr_t load_pa, uintptr_t dtb_pa);
-void relocate_enable_mmu(pgd_t *pgdir);
-void relocate_kernel(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LUME_SETUP_H */
diff --git a/na/include/lume/signal.h b/na/include/lume/signal.h
deleted file mode 100644
index 05b7542..0000000
--- a/na/include/lume/signal.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Signal Infrastructure — Core signal delivery API
- *
- * Provides the interface between trap handling, signal delivery,
- * and user-space signal handler execution.
- *
- * Reference: Linux include/linux/sched/signal.h
- */
-
-#include <lume/types.h>
-
-struct TaskControlBlock;
-struct sighand_struct;
-namespace arch { struct TrapFrame; }
-
-/* ------------------------------------------------------------------
- * sighand_struct lifecycle (Linux kernel/fork.c:copy_sighand pattern)
- * ------------------------------------------------------------------ */
-
-sighand_struct* alloc_sighand();
-sighand_struct* copy_sighand(sighand_struct* parent);
-void get_sighand(sighand_struct* sh);
-void put_sighand(sighand_struct* sh);
-
-/*
- * force_sig_fault — Deliver a fault-induced signal to the current task.
- *
- * Called from trap handler when a user-mode exception cannot be resolved.
- * Bypasses the blocked mask and enqueues the signal.
- *
- * @signo: Signal number (SIGSEGV, SIGBUS, SIGILL, SIGFPE, etc.)
- * @code: si_code for siginfo_t (e.g., SEGV_MAPERR, SEGV_ACCERR)
- * @addr: Fault address (stval / badaddr)
- */
-void force_sig_fault(int signo, int code, uint64 addr);
-
-/*
- * do_signal — Process pending signals before returning to user mode.
- *
- * Called at the end of trap_handle() when origin == FromUser.
- * If a signal is pending and a handler is registered, constructs
- * a signal frame on the user stack and redirects epc.
- *
- * @tf: TrapFrame to modify if signal delivery is needed.
- */
-void do_signal(arch::TrapFrame* tf);
-
-/*
- * signal_pending — Check if the task has unblocked pending signals.
- *
- * Used by blocking syscalls (e.g., n_tty_read) to detect whether
- * they should return -EINTR.
- */
-bool signal_pending(TaskControlBlock* task);
-
diff --git a/na/include/lume/signal_nr.h b/na/include/lume/signal_nr.h
deleted file mode 100644
index 8e595d7..0000000
--- a/na/include/lume/signal_nr.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Linux-compatible signal numbers and related ABI constants.
- *
- * This mirrors the common Linux UAPI numbering used by asm-generic/signal.h.
- * Keep the values stable so user-space binaries see the same numbers.
- */
-
-inline constexpr int _NSIG = 64;
-inline constexpr int _NSIG_BPW = sizeof(unsigned long) * 8;
-inline constexpr int _NSIG_WORDS = _NSIG / _NSIG_BPW;
-
-inline constexpr int SIGHUP = 1;
-inline constexpr int SIGINT = 2;
-inline constexpr int SIGQUIT = 3;
-inline constexpr int SIGILL = 4;
-inline constexpr int SIGTRAP = 5;
-inline constexpr int SIGABRT = 6;
-inline constexpr int SIGIOT = SIGABRT;
-inline constexpr int SIGBUS = 7;
-inline constexpr int SIGFPE = 8;
-inline constexpr int SIGKILL = 9;
-inline constexpr int SIGUSR1 = 10;
-inline constexpr int SIGSEGV = 11;
-inline constexpr int SIGUSR2 = 12;
-inline constexpr int SIGPIPE = 13;
-inline constexpr int SIGALRM = 14;
-inline constexpr int SIGTERM = 15;
-inline constexpr int SIGSTKFLT = 16;
-inline constexpr int SIGCHLD = 17;
-inline constexpr int SIGCONT = 18;
-inline constexpr int SIGSTOP = 19;
-inline constexpr int SIGTSTP = 20;
-inline constexpr int SIGTTIN = 21;
-inline constexpr int SIGTTOU = 22;
-inline constexpr int SIGURG = 23;
-inline constexpr int SIGXCPU = 24;
-inline constexpr int SIGXFSZ = 25;
-inline constexpr int SIGVTALRM = 26;
-inline constexpr int SIGPROF = 27;
-inline constexpr int SIGWINCH = 28;
-inline constexpr int SIGIO = 29;
-inline constexpr int SIGPOLL = SIGIO;
-inline constexpr int SIGPWR = 30;
-inline constexpr int SIGSYS = 31;
-inline constexpr int SIGUNUSED = 31;
-
-/* Real-time signals start here in Linux UAPI. */
-inline constexpr int SIGRTMIN = 32;
-inline constexpr int SIGRTMAX = _NSIG;
diff --git a/na/include/lume/signal_types.h b/na/include/lume/signal_types.h
deleted file mode 100644
index f66231b..0000000
--- a/na/include/lume/signal_types.h
+++ /dev/null
@@ -1,263 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Signal Types — Linux-compatible structures for signal delivery
- *
- * Follows the RISC-V LP64 ABI layout so that user-space programs
- * compiled with musl / glibc see the correct struct sizes.
- *
- * Reference: Linux include/uapi/asm-generic/signal.h
- * Linux include/uapi/asm-generic/siginfo.h
- */
-
-#include <lume/types.h>
-#include <lume/signal_nr.h>
-#include <lume/list.h>
-#include <kernel/sync/spinlock.h>
-
-/* ================================================================== */
-/* Signal set (64-bit bitmap) */
-/* ================================================================== */
-
-struct sigset_t {
- uint64 sig[_NSIG_WORDS]; /* _NSIG_WORDS = 1 on LP64 */
-
- void clear() { for (auto& s : sig) s = 0; }
- void fill() { for (auto& s : sig) s = ~0ULL; }
- void add(int signo) { sig[(signo-1)/64] |= (1ULL << ((signo-1)%64)); }
- void del(int signo) { sig[(signo-1)/64] &= ~(1ULL << ((signo-1)%64)); }
- bool has(int signo) const { return (sig[(signo-1)/64] >> ((signo-1)%64)) & 1; }
- bool is_empty() const { for (auto s : sig) if (s) return false; return true; }
-
- sigset_t operator&(const sigset_t& o) const {
- sigset_t r;
- for (int i = 0; i < _NSIG_WORDS; i++) r.sig[i] = sig[i] & o.sig[i];
- return r;
- }
- sigset_t operator|(const sigset_t& o) const {
- sigset_t r;
- for (int i = 0; i < _NSIG_WORDS; i++) r.sig[i] = sig[i] | o.sig[i];
- return r;
- }
- sigset_t operator~() const {
- sigset_t r;
- for (int i = 0; i < _NSIG_WORDS; i++) r.sig[i] = ~sig[i];
- return r;
- }
-};
-
-/* ================================================================== */
-/* siginfo_t (simplified, Linux UAPI compatible layout) */
-/* ================================================================== */
-
-struct kernel_siginfo {
- int si_signo;
- int si_errno;
- int si_code;
- int _pad0; /* alignment */
-
- union {
- /* kill() */
- struct {
- int32 _pid;
- int32 _uid;
- } _kill;
-
- /* SIGCHLD */
- struct {
- int32 _pid;
- int32 _uid;
- int _status;
- long _utime;
- long _stime;
- } _sigchld;
-
- /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP */
- struct {
- uint64 _addr;
- } _sigfault;
-
- /* Pad to 128 bytes total (Linux SI_MAX_SIZE) */
- char _pad[128 - 4*sizeof(int)];
- } _sifields;
-};
-
-/* ================================================================== */
-/* Signal action (kernel representation) */
-/* ================================================================== */
-
-/* sa_flags bits (Linux UAPI) */
-inline constexpr uint64 SA_NOCLDSTOP = 0x00000001;
-inline constexpr uint64 SA_NOCLDWAIT = 0x00000002;
-inline constexpr uint64 SA_SIGINFO = 0x00000004;
-inline constexpr uint64 SA_RESTORER = 0x04000000;
-inline constexpr uint64 SA_ONSTACK = 0x08000000;
-inline constexpr uint64 SA_RESTART = 0x10000000;
-inline constexpr uint64 SA_NODEFER = 0x40000000;
-inline constexpr uint64 SA_RESETHAND = 0x80000000;
-
-/* Special handler values */
-inline constexpr uint64 SIG_DFL = 0;
-inline constexpr uint64 SIG_IGN = 1;
-inline constexpr uint64 SIG_ERR = static_cast<uint64>(-1);
-
-struct kernel_sigaction {
- uint64 sa_handler; /* handler address or SIG_DFL/SIG_IGN */
- uint64 sa_flags;
- uint64 sa_restorer; /* sigreturn trampoline (SA_RESTORER) */
- sigset_t sa_mask; /* signals blocked during handler */
-};
-
-/* ================================================================== */
-/* Per-task signal state */
-/* ================================================================== */
-
-/* Queued signal entry */
-struct sigqueue {
- list_node list;
- kernel_siginfo info;
-};
-
-/* Pending signal set + queue */
-struct sigpending {
- list_node list; /* list of sigqueue entries */
- sigset_t signal; /* bitmap of pending signals */
-
- void init() {
- list.init();
- signal.clear();
- }
-};
-
-/* Signal handler table (shared between threads via CLONE_SIGHAND).
- *
- * Linux pattern (kernel/fork.c:copy_sighand): one sighand_struct per
- * thread group by default, shared across CLONE_SIGHAND siblings,
- * refcounted, freed when the last referencing task exits. Its
- * `siglock` guards BOTH the action table and every per-task `pending`
- * list of every task that references this sighand. That single-lock
- * model is what lets send/dequeue/discard race-free without per-list
- * locks. */
-struct sighand_struct {
- Spinlock siglock;
- uint32 refcount; /* # tasks sharing this sighand (≥1) */
- kernel_sigaction action[_NSIG];
-
- void init() {
- siglock.init("sighand");
- refcount = 1;
- for (auto& a : action) {
- a.sa_handler = SIG_DFL;
- a.sa_flags = 0;
- a.sa_restorer = 0;
- a.sa_mask.clear();
- }
- }
-};
-
-/* ================================================================== */
-/* signal_struct — per-thread-group shared state */
-/* ================================================================== */
-
-struct TaskControlBlock;
-struct tty_struct;
-
-/*
- * Linux's signal_struct is the per-thread-group container holding:
- * - Process group / session identity (pgid, sid)
- * - Controlling terminal (tty)
- * - Misc state shared across CLONE_THREAD siblings
- *
- * Refcount tracks the number of tasks in the thread group; when the last
- * task in the group exits, the signal_struct is freed. Detachment from
- * the pgrp hash also happens at that point.
- *
- * `pgrp_link` makes this signal_struct a member of one pgrp bucket; the
- * pgrp subsystem walks the bucket to enumerate all thread groups whose
- * pgid hashes there.
- */
-struct signal_struct {
- Spinlock lock;
- uint32 refcount; /* # tasks sharing this (≥1) */
- int32 pgid; /* process group id */
- int32 sid; /* session id */
- tty_struct* tty; /* controlling tty (or nullptr) */
- int32 tty_old_pgrp; /* SIGHUP target on TIOCNOTTY */
- TaskControlBlock* leader; /* thread-group leader (pid==tgid) */
- list_node pgrp_link; /* on pgrp hash bucket */
-
- void init(int32 leader_pid, TaskControlBlock* tg_leader) {
- lock.init("signal");
- refcount = 1;
- pgid = leader_pid;
- sid = leader_pid;
- tty = nullptr;
- tty_old_pgrp = 0;
- leader = tg_leader;
- pgrp_link.init();
- }
-};
-
-/* ================================================================== */
-/* Default signal dispositions */
-/* ================================================================== */
-
-enum SigDefaultAction {
- SIG_ACT_TERM, /* Terminate */
- SIG_ACT_IGN, /* Ignore */
- SIG_ACT_CORE, /* Terminate + core dump */
- SIG_ACT_STOP, /* Stop process */
- SIG_ACT_CONT, /* Continue if stopped */
-};
-
-/* Returns the default action for a standard signal */
-inline SigDefaultAction sig_default_action(int sig)
-{
- switch (sig) {
- case SIGCHLD:
- case SIGURG:
- case SIGWINCH:
- return SIG_ACT_IGN;
-
- case SIGCONT:
- return SIG_ACT_CONT;
-
- case SIGSTOP:
- case SIGTSTP:
- case SIGTTIN:
- case SIGTTOU:
- return SIG_ACT_STOP;
-
- case SIGQUIT:
- case SIGILL:
- case SIGTRAP:
- case SIGABRT:
- case SIGBUS:
- case SIGFPE:
- case SIGSEGV:
- case SIGXCPU:
- case SIGXFSZ:
- case SIGSYS:
- return SIG_ACT_CORE;
-
- default:
- return SIG_ACT_TERM;
- }
-}
-
-/* SIG_KERNEL_ONLY: cannot be caught or ignored */
-inline bool sig_kernel_only(int sig)
-{
- return sig == SIGKILL || sig == SIGSTOP;
-}
-
-/* ================================================================== */
-/* SigprocMask how values (Linux UAPI) */
-/* ================================================================== */
-
-inline constexpr int SIG_BLOCK = 0;
-inline constexpr int SIG_UNBLOCK = 1;
-inline constexpr int SIG_SETMASK = 2;
diff --git a/na/include/lume/slab.h b/na/include/lume/slab.h
deleted file mode 100644
index 4ebade1..0000000
--- a/na/include/lume/slab.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * SLUB public API
- *
- * This header exposes the allocator entry points to the rest of the kernel.
- * Implementation details stay in mm/slab.h and mm/slab.cc.
- */
-
-#include <lume/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Initialize global slab caches. */
-void slab_init();
-
-/* Allocate/free kernel memory. */
-void* kmalloc(uint32 size);
-void kfree(void* ptr);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/na/include/lume/smp.h b/na/include/lume/smp.h
deleted file mode 100644
index a5292b6..0000000
--- a/na/include/lume/smp.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * smp.h — Machine-Independent SMP primitives
- *
- * Provides cross-CPU communication (IPI) and TLB coordination.
- * The MI interface calls through to arch-specific backends
- * (e.g. SBI on RISC-V, APIC on x86).
- *
- * Reference: docs/specs/linux_abi_mm_plan.md §SMP
- */
-
-#include <lume/types.h>
-#include <lume/atomic.h>
-#include <lume/config.h>
-
-/*
- * IPI reasons — each bit in the per-CPU pending mask
- * corresponds to one reason. Multiple reasons can be
- * pending simultaneously.
- */
-enum IpiReason : uint32 {
- IPI_RESCHEDULE = (1U << 0), /* Target should call schedule() */
- IPI_TLB_SHOOTDOWN = (1U << 1), /* Target should flush TLB */
- IPI_CALL_FUNCTION = (1U << 2), /* Target should run a function */
- IPI_HALT = (1U << 3), /* Target should halt */
-};
-
-/*
- * Per-CPU IPI pending state.
- * Written by sender (atomically), consumed by receiver in IRQ context.
- */
-struct IpiData {
- lume::atomic<uint32> pending; /* Bitmask of IpiReason */
-
- /* TLB shootdown payload */
- uint64 tlb_start;
- uint64 tlb_end;
-};
-
-/* Per-CPU IPI data array (BSS zero-initialized) */
-extern IpiData g_ipi_data[kMaxCpus];
-
-/*
- * smp_send_ipi — Send an IPI to a set of CPUs
- *
- * @cpu_mask: bitmask of target CPU IDs (bit N = CPU N)
- * @reason: reason bits to set in the target's pending mask
- *
- * Safe to call from any context (interrupt or process).
- */
-void smp_send_ipi(uint64 cpu_mask, uint32 reason);
-
-/*
- * smp_send_reschedule — Convenience: send reschedule IPI to one CPU
- */
-inline void smp_send_reschedule(uint8 cpu_id)
-{
- smp_send_ipi(1ULL << cpu_id, IPI_RESCHEDULE);
-}
-
-/*
- * smp_init — Initialize SMP subsystem (called from BSP after trap_init)
- */
-void smp_init();
-
-/*
- * smp_handle_ipi — Called from trap handler on receiving S-mode
- * software interrupt. Processes all pending IPI reasons.
- */
-void smp_handle_ipi();
diff --git a/na/include/lume/stdarg.h b/na/include/lume/stdarg.h
deleted file mode 100644
index c8dc7f4..0000000
--- a/na/include/lume/stdarg.h
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-#ifndef _LINUX_STDARG_H
-#define _LINUX_STDARG_H
-
-typedef __builtin_va_list va_list;
-#define va_start(v, l) __builtin_va_start(v, l)
-#define va_end(v) __builtin_va_end(v)
-#define va_arg(v, T) __builtin_va_arg(v, T)
-#define va_copy(d, s) __builtin_va_copy(d, s)
-
-#endif
diff --git a/na/include/lume/stringify.h b/na/include/lume/stringify.h
deleted file mode 100644
index 0e84cbe..0000000
--- a/na/include/lume/stringify.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef __LINUX_STRINGIFY_H
-#define __LINUX_STRINGIFY_H
-
-/* Indirect stringification. Doing two levels allows the parameter to be a
- * macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
- * converts to "bar".
- */
-
-#define __stringify_1(x...) #x
-#define __stringify(x...) __stringify_1(x)
-
-#define FILE_LINE __FILE__ ":" __stringify(__LINE__)
-
-#endif /* !__LINUX_STRINGIFY_H */
diff --git a/na/include/lume/syscall.h b/na/include/lume/syscall.h
deleted file mode 100644
index 8185096..0000000
--- a/na/include/lume/syscall.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * System Call Interface — Machine-Independent Layer
- *
- * Defines the unified syscall handler signature and the MI dispatch function.
- * MD layer (arch/riscv/syscall/syscall_md.cc) extracts registers into args[6]
- * and calls syscall_dispatch().
- *
- * Reference: docs/specs/syscall.md §4
- */
-
-#include <lume/types.h>
-
-/* Unified syscall handler signature.
- * All arguments and return value are register-width (uint64).
- * Individual sys_* functions cast internally. */
-using SyscallHandler = int64 (*)(uint64, uint64, uint64,
- uint64, uint64, uint64);
-
-/* MI-layer syscall dispatcher.
- * Called by MD layer after extracting syscall number and arguments.
- * Returns the syscall result (negative errno on error). */
-int64 syscall_dispatch(uint64 nr, uint64 args[6]);
-
-/* Register all implemented syscall handlers into the jump table.
- * Called from kernel_main() after sched_init(). */
-void syscall_init();
-
-/* MD-layer entry point — called from trap handler on TrapCause::Syscall.
- * Extracts registers from TrapFrame, calls syscall_dispatch, writes result. */
-namespace arch {
-struct TrapFrame;
-}
-void syscall_dispatch_md(arch::TrapFrame* tf);
diff --git a/na/include/lume/syscall_nr.h b/na/include/lume/syscall_nr.h
deleted file mode 100644
index 29e3904..0000000
--- a/na/include/lume/syscall_nr.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * System Call Numbers — RISC-V LP64 (asm-generic/unistd.h compatible)
- *
- * Numbering strictly follows Linux asm-generic so that musl libc / Busybox
- * binaries can be run without modification.
- *
- * Reference: docs/specs/syscall.md §4.3
- */
-
-inline constexpr int SYS_exit = 93;
-inline constexpr int SYS_exit_group = 94;
-inline constexpr int SYS_set_tid_address = 96;
-inline constexpr int SYS_sched_yield = 124;
-inline constexpr int SYS_uname = 160;
-inline constexpr int SYS_getpid = 172;
-inline constexpr int SYS_getppid = 173;
-inline constexpr int SYS_getuid = 174;
-inline constexpr int SYS_getgid = 176;
-inline constexpr int SYS_gettid = 178;
-inline constexpr int SYS_brk = 214;
-inline constexpr int SYS_munmap = 215;
-inline constexpr int SYS_clone = 220;
-inline constexpr int SYS_execve = 221;
-inline constexpr int SYS_mmap = 222;
-inline constexpr int SYS_mprotect = 226;
-inline constexpr int SYS_wait4 = 260;
-
-inline constexpr int SYS_getcwd = 17;
-inline constexpr int SYS_dup = 23;
-inline constexpr int SYS_dup3 = 24;
-inline constexpr int SYS_ioctl = 29;
-inline constexpr int SYS_mkdirat = 34;
-inline constexpr int SYS_unlinkat = 35;
-inline constexpr int SYS_chdir = 49;
-inline constexpr int SYS_openat = 56;
-inline constexpr int SYS_close = 57;
-inline constexpr int SYS_pipe2 = 59;
-inline constexpr int SYS_lseek = 62;
-inline constexpr int SYS_read = 63;
-inline constexpr int SYS_write = 64;
-inline constexpr int SYS_writev = 66;
-inline constexpr int SYS_ppoll = 73;
-inline constexpr int SYS_fstatat = 79;
-inline constexpr int SYS_fstat = 80;
-inline constexpr int SYS_getdents64 = 61;
-
-inline constexpr int SYS_fcntl = 25;
-inline constexpr int SYS_linkat = 37;
-inline constexpr int SYS_mount = 40;
-inline constexpr int SYS_umount2 = 39;
-
-inline constexpr int SYS_kill = 129;
-inline constexpr int SYS_tkill = 130;
-inline constexpr int SYS_tgkill = 131;
-inline constexpr int SYS_sigaltstack = 132;
-inline constexpr int SYS_rt_sigaction = 134;
-inline constexpr int SYS_rt_sigprocmask = 135;
-inline constexpr int SYS_rt_sigreturn = 139;
-inline constexpr int SYS_reboot = 142;
-inline constexpr int SYS_setpgid = 154;
-inline constexpr int SYS_getpgid = 155;
-inline constexpr int SYS_getsid = 156;
-inline constexpr int SYS_setsid = 157;
-inline constexpr int SYS_prctl = 167;
-inline constexpr int SYS_geteuid = 175;
-inline constexpr int SYS_getegid = 177;
-
-inline constexpr int SYS_futex = 98;
-inline constexpr int SYS_nanosleep = 101;
-inline constexpr int SYS_clock_gettime = 113;
-inline constexpr int SYS_epoll_create1 = 20;
-inline constexpr int SYS_epoll_ctl = 21;
-inline constexpr int SYS_epoll_pwait = 22;
-
-inline constexpr int SYS_rt_sigtimedwait = 137;
-inline constexpr int SYS_socket = 198;
-inline constexpr int SYS_sendto = 206;
diff --git a/na/include/lume/task.h b/na/include/lume/task.h
deleted file mode 100644
index e959147..0000000
--- a/na/include/lume/task.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Task Control Block & Task State
- *
- * Central representation of every execution context in LumeOS,
- * whether kernel thread or (future) user process.
- *
- * Reference: docs/specs/task.md §3.1, docs/specs/scheduler.md §3.1
- */
-
-#include <lume/types.h>
-#include <lume/config.h>
-#include <lume/list.h>
-#include <arch/context.h>
-#include <arch/trapframe.h>
-#include <arch/fpstate.h>
-#include <lume/waitqueue.h>
-#include <lume/signal_types.h>
-
-/* Forward declarations */
-struct VmSpace;
-struct SchedClass;
-
-/* ========================================================================
- * Task State Machine
- * ======================================================================== */
-
-enum class TaskState : uint8 {
- Runnable = 0, // In a Per-CPU ready queue
- Running = 1, // Currently executing on a CPU
- Sleeping = 2, // Blocked on a WaitQueue
- Zombie = 3, // Exited, waiting for parent wait()
- Dead = 4, // Fully reclaimed, TCB can be freed
- Stopped = 5, // Job control: SIGSTOP/SIGTSTP/SIGTTIN/SIGTTOU
-};
-
-/* ========================================================================
- * Task Flags
- * ======================================================================== */
-
-inline constexpr uint8 TF_KTHREAD = 0x01; // Kernel thread (never enters user mode)
-inline constexpr uint8 TF_IDLE = 0x02; // Idle thread
-inline constexpr uint8 TF_INIT = 0x04; // init process (PID 1)
-
-/* ========================================================================
- * Kernel Stack
- * ======================================================================== */
-
-inline constexpr uint64 kKernelStackSize = CONFIG_KERNEL_STACK_SIZE;
-inline constexpr int kKernelStackOrder = (kKernelStackSize <= 4096) ? 0
- : (kKernelStackSize <= 8192) ? 1
- : (kKernelStackSize <= 16384) ? 2
- : (kKernelStackSize <= 32768) ? 3 : 4;
-inline constexpr uint64 kStackCanary = 0xDEAD'BEEF'CAFE'BABEULL;
-
-/* ========================================================================
- * Task Control Block
- * ======================================================================== */
-
-struct TaskControlBlock {
- /* ===== Scheduler hot fields (cache-line optimized) ===== */
- TaskState state; // Current state
- uint8 priority; // Static priority [0, 31]
- uint8 cpu_id; // Pinned CPU (Phase 2: no migration)
- uint8 flags; // TF_KTHREAD | TF_IDLE | TF_INIT
-
- int32 time_slice; // Remaining time slice (ticks)
- int32 pid; // Unique task ID (Linux: tid)
- int32 tgid; // Thread group ID (Linux: pid)
- int32 preempt_count; // >0: preemption disabled (holding locks etc.)
-
- list_node run_link; // RunQueue list node
- list_node all_link; // Global task list node
-
- /* ===== Context ===== */
- arch::Context context; // Callee-saved registers (scheduler switch)
- arch::TrapFrame* trapframe; // Trap frame (user-mode entry/exit)
- arch::FpState fpstate; // FPU register state (f0-f31 + fcsr)
-
- /* ===== Kernel Stack ===== */
- uint64 kstack_pa; // Physical address
- uint64 kstack_va; // Virtual address (stack bottom)
-
- /* ===== Address Space ===== */
- VmSpace* mm; // nullptr = kernel thread
-
- /* ===== Per-task lock =====
- * Protects: parent, children, sibling, exit_code, state transitions.
- * Lock ordering: task->lock -> g_task_list_lock (never reverse). */
- Spinlock lock;
-
- /* ===== Task Hierarchy ===== */
- TaskControlBlock* parent;
- list_node children; // Children list head
- list_node sibling; // Sibling list node
- WaitQueue wait_chld; // Parent waits on this for children to exit
-
- /* ===== Exit ===== */
- int32 exit_code;
- int32 exit_signal; // Linux: signal sent to parent on exit.
- // SIGCHLD = notify parent, become zombie
- // -1 = auto-reap (kthreads)
- uint64 clear_child_tid; // CLONE_CHILD_CLEARTID: write 0 here on exit
-
- /* ===== Job control reporting =====
- * stopped_code: 0 means "no pending stop notification"; non-zero is the
- * Linux wstatus value to hand back from wait4(WUNTRACED). Cleared
- * after the parent consumes it via wait4 (or task termination).
- * notify_continued: set when SIGCONT resumes us; consumed by
- * wait4(WCONTINUED). */
- int32 stopped_code;
- bool notify_continued;
-
- /* ===== Scheduler class metadata ===== */
- const SchedClass* sched_class;
- void* sched_entity;
-
- /* ===== VFS ===== */
- struct files_struct* files; // Per-task fd table (shared via CLONE_FILES)
- struct fs_struct* fs; // cwd/root (shared via CLONE_FS)
-
- /* ===== Signals ===== */
- struct sigpending pending; // Per-task pending signal queue
- sigset_t blocked; // Blocked signal mask
- struct sighand_struct* sighand; // Signal handler table (shared via CLONE_SIGHAND)
- struct signal_struct* signal; // Thread-group identity: pgid/sid/ctty (shared via CLONE_THREAD)
-
- /* ===== Debug ===== */
- const char* name;
-};
-
-/* ========================================================================
- * current_task() — fast access to the running task on this CPU
- *
- * Must be called with interrupts disabled or while holding a spinlock.
- * ======================================================================== */
-TaskControlBlock* current_task();
-
-/* Terminate the current task with a signal. */
-void task_kill_current(int sig);
diff --git a/na/include/lume/timer.h b/na/include/lume/timer.h
deleted file mode 100644
index 9bdddd5..0000000
--- a/na/include/lume/timer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-#include <lume/types.h>
-
-/*
- * MI Timer Subsystem
- */
-
-#include <lume/list.h>
-
-struct TimerList {
- list_node entry;
- uint64 expires;
- void (*function)(TimerList*);
- void* data;
-
- void init(void (*cb)(TimerList*)) {
- entry.init();
- function = cb;
- data = nullptr;
- expires = 0;
- }
-};
-
-extern uint64 g_jiffies;
-
-void timer_init();
-
-// AP per-CPU initialization
-void timer_init_ap();
-
-// Handle timer interrupt tick (called from TrapHandler)
-void timer_tick();
-
-// Get configured timebase frequency
-uint64 timer_get_freq();
-
-void add_timer(TimerList* timer);
-int del_timer(TimerList* timer);
-int mod_timer(TimerList* timer, uint64 expires);
diff --git a/na/include/lume/tlb.h b/na/include/lume/tlb.h
deleted file mode 100644
index b4d51ea..0000000
--- a/na/include/lume/tlb.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * tlb.h — Machine-Independent TLB management interface
- *
- * All TLB invalidation in the kernel MUST go through these functions.
- * Direct use of arch::mmu::flush_tlb_*() is prohibited outside of
- * this file's implementation.
- *
- * Current implementation: local CPU sfence.vma only (single-core).
- * When SMP IPI infrastructure is available, tlb_shootdown_range()
- * and tlb_shootdown_all() will broadcast invalidation to remote
- * CPUs via IPI — no caller changes required.
- *
- * Reference: docs/specs/linux_abi_mm_plan.md §4
- */
-
-#include <lume/types.h>
-
-struct VmSpace;
-
-/*
- * Invalidate TLB entries for a VA range [start, end) on ALL CPUs
- * that may have cached translations for the given address space.
- *
- * @mm: address space (used to determine which CPUs to IPI;
- * nullptr means kernel address space = all CPUs).
- * @start: first VA to invalidate (page-aligned).
- * @end: one-past-last VA to invalidate (page-aligned).
- *
- * If (end - start) exceeds a threshold, falls back to full flush.
- */
-void tlb_shootdown_range(VmSpace* mm, uint64 start, uint64 end);
-
-/*
- * Invalidate a single page's TLB entry on ALL CPUs.
- * Convenience wrapper around tlb_shootdown_range().
- */
-void tlb_shootdown_page(VmSpace* mm, uint64 va);
-
-/*
- * Full TLB flush on ALL CPUs.
- * Used after page table switch, large-scale munmap, etc.
- */
-void tlb_shootdown_all();
diff --git a/na/include/lume/trap.h b/na/include/lume/trap.h
deleted file mode 100644
index 61a5054..0000000
--- a/na/include/lume/trap.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Trap Subsystem — Machine-Independent Interface
- *
- * Defines TrapCause enum and MI-layer declarations.
- * MD layer (arch/riscv/trap/trap_md.cc) translates scause → TrapCause.
- * MI layer (kernel/trap.cc) dispatches based on TrapCause alone.
- *
- * Reference: docs/specs/trap.md §3.2
- */
-
-#include <lume/types.h>
-
-/* MI-layer trap cause classification.
- * MD layer is responsible for translating architecture-specific cause codes
- * (e.g., RISC-V scause) into these semantic values. MI layer never reads CSRs. */
-enum class TrapCause : uint32 {
- /* --- Exceptions (Synchronous) --- */
- PageFaultLoad = 0, // Load page fault
- PageFaultStore = 1, // Store/AMO page fault
- PageFaultExec = 2, // Instruction page fault
- Syscall = 3, // User-mode ecall
- IllegalInst = 4, // Illegal instruction
- Breakpoint = 5, // Breakpoint (ebreak)
- AccessFault = 6, // Bus access fault (not page fault)
- AlignFault = 7, // Address misalignment
-
- /* --- Interrupts (Asynchronous) --- */
- IrqTimer = 16, // S-mode timer interrupt
- IrqSoftware = 17, // S-mode software interrupt (IPI)
- IrqExternal = 18, // S-mode external interrupt (PLIC)
-
- /* --- Unknown --- */
- Unknown = 255,
-};
-
-/* High-level trap class for routing and stats. */
-enum class TrapClass : uint8 {
- Exception = 0,
- Interrupt = 1,
- Syscall = 2,
- Unknown = 3,
-};
-
-/* Origin privilege of the trap. */
-enum class TrapOrigin : uint8 {
- FromUser = 0,
- FromKernel = 1,
-};
-
-/* Unified trap context passed from MD to MI. */
-struct TrapInfo {
- TrapClass cls;
- TrapCause cause;
- TrapOrigin origin;
- uint64 val; // stval (faulting address or instruction)
- uint64 epc; // sepc at trap entry (may be adjusted by MD)
- uint64 scause; // raw scause for diagnostics
-};
-
-/* Trap statistics for debugging/testing. */
-inline constexpr uint32 kTrapCauseCount = 12;
-
-struct TrapStats {
- uint64 total;
- uint64 by_cause[kTrapCauseCount];
- TrapCause last_cause;
- TrapClass last_class;
- TrapOrigin last_origin;
- uint64 last_val;
- uint64 last_epc;
- uint64 last_scause;
-};
-
-namespace arch {
-struct TrapFrame; // Forward declaration (defined in arch/trapframe.h)
-}
-
-/*
- * trap_init() — BSP initialization
- * Sets stvec to kernel_trap_entry (handles traps while in S-mode).
- * Initializes the global IrqManager.
- * Called from kernel_main() after VMM and before PLIC.
- */
-void trap_init();
-
-/*
- * trap_init_ap() — AP per-CPU initialization
- * Sets this CPU's stvec.
- */
-void trap_init_ap();
-
-/*
- * trap_handle() — MI-layer semantic dispatcher
- * Called by MD layer after scause → TrapCause translation.
- * Routes to page fault handler, syscall dispatcher, IRQ manager, etc.
- *
- * tf: pointer to the current TrapFrame
- * info: translated trap context from MD layer
- */
-void trap_handle(arch::TrapFrame* tf, const TrapInfo& info);
-
-/* Trap statistics helpers (MI layer). */
-void trap_stats_get(TrapStats* out);
-void trap_stats_reset();
diff --git a/na/include/lume/tty.h b/na/include/lume/tty.h
deleted file mode 100644
index 34cf7bb..0000000
--- a/na/include/lume/tty.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * tty.h — TTY subsystem core structures
- *
- * tty_struct is the central object representing an open terminal.
- * It connects a hardware driver (tty_driver) with a line discipline
- * (tty_ldisc) to implement the full terminal abstraction.
- *
- * Architecture:
- * user read/write → tty_core → tty_ldisc (n_tty) → tty_driver (uart/pty)
- *
- * Reference: Linux include/linux/tty.h, include/linux/tty_ldisc.h
- */
-
-#include <lume/types.h>
-#include <lume/tty_ioctl.h>
-#include <lume/waitqueue.h>
-#include <kernel/sync/spinlock.h>
-
-struct tty_struct;
-struct file;
-struct file_operations;
-struct inode;
-
-/* ================================================================== */
-/* tty_driver — hardware/pseudo-terminal driver interface */
-/* ================================================================== */
-
-struct tty_driver_ops {
- int (*open)(tty_struct* tty);
- void (*close)(tty_struct* tty);
- int (*write)(tty_struct* tty, const char* buf, int count);
- int (*write_room)(tty_struct* tty);
- void (*put_char)(tty_struct* tty, char ch);
- void (*flush_chars)(tty_struct* tty);
- int (*chars_in_buffer)(tty_struct* tty);
- void (*flush_buffer)(tty_struct* tty);
- int (*ioctl)(tty_struct* tty, unsigned int cmd, uint64 arg);
- void (*throttle)(tty_struct* tty);
- void (*unthrottle)(tty_struct* tty);
- void (*hangup)(tty_struct* tty);
-};
-
-struct tty_driver {
- const char* name; /* "ttyS", "ttySIF", "pts" */
- int major;
- int minor_start;
- int num; /* number of devices */
- const tty_driver_ops* ops;
-};
-
-/* ================================================================== */
-/* tty_ldisc — line discipline interface */
-/* ================================================================== */
-
-struct tty_ldisc_ops {
- const char* name;
- int num; /* N_TTY = 0 */
-
- int (*open)(tty_struct* tty);
- void (*close)(tty_struct* tty);
- int (*read)(tty_struct* tty, file* fp, char* buf, uint64 count);
- int (*write)(tty_struct* tty, file* fp, const char* buf, uint64 count);
- void (*receive_buf)(tty_struct* tty, const char* buf, int count);
- int (*ioctl)(tty_struct* tty, unsigned int cmd, uint64 arg);
- void (*flush_buffer)(tty_struct* tty);
- int (*chars_in_buffer)(tty_struct* tty);
- void (*set_termios)(tty_struct* tty, const termios* old);
- void (*hangup)(tty_struct* tty);
-};
-
-struct tty_ldisc {
- const tty_ldisc_ops* ops;
- void* data; /* ldisc private data (n_tty_data*) */
-};
-
-/* ================================================================== */
-/* tty_struct — central TTY object */
-/* ================================================================== */
-
-inline constexpr int TTY_BUF_SIZE = 4096;
-
-/* TTY flags */
-inline constexpr uint32 TTY_THROTTLED = (1 << 0);
-inline constexpr uint32 TTY_IO_ERROR = (1 << 1);
-inline constexpr uint32 TTY_CLOSING = (1 << 2);
-inline constexpr uint32 TTY_HUPPING = (1 << 3);
-
-struct tty_struct {
- int index; /* device index */
- const tty_driver* driver; /* hardware driver */
- tty_ldisc ldisc; /* current line discipline */
- tty_struct* link; /* PTY peer (master ↔ slave) */
-
- termios termios_data; /* terminal settings */
- winsize ws; /* window size */
-
- int32 pgrp; /* foreground process group */
- int32 session; /* controlling session */
-
- /* Output buffer (tty_core → driver) */
- char write_buf[TTY_BUF_SIZE];
- int write_cnt;
-
- /* Synchronization */
- WaitQueue read_wait;
- WaitQueue write_wait;
- Spinlock lock;
-
- uint32 flags;
- int open_count;
-
- /* Private driver data */
- void* driver_data;
-};
-
-/* ================================================================== */
-/* TTY core API */
-/* ================================================================== */
-
-/* Initialize the TTY subsystem (creates /dev/console TTY) */
-void tty_init();
-
-/* Allocate and initialize a new tty_struct */
-tty_struct* tty_alloc(const tty_driver* driver, int index);
-
-/* Open/close a TTY (called from chardev open/close) */
-int tty_open(tty_struct* tty);
-void tty_close(tty_struct* tty);
-
-/* Core I/O (called from file_operations) */
-int64 tty_read(tty_struct* tty, file* fp, char* buf, uint64 count);
-int64 tty_write(tty_struct* tty, file* fp, const char* buf, uint64 count);
-long tty_ioctl(tty_struct* tty, file* fp, unsigned int cmd, uint64 arg);
-
-/* Called by hardware driver when bytes arrive */
-void tty_receive_buf(tty_struct* tty, const char* buf, int count);
-
-/* Signal helpers */
-void tty_signal_pgrp(tty_struct* tty, int sig);
-
-/* file_operations for TTY devices */
-extern file_operations tty_fops;
-
-/* Global console TTY */
-extern tty_struct* g_console_tty;
-
-/* ================================================================== */
-/* N_TTY line discipline */
-/* ================================================================== */
-
-extern const tty_ldisc_ops n_tty_ops;
-
-/* ================================================================== */
-/* UART TTY driver */
-/* ================================================================== */
-
-extern const tty_driver uart_tty_driver;
-
-/* Initialize UART as a TTY driver (registers RX IRQ) */
-void uart_tty_init(tty_struct* tty);
diff --git a/na/include/lume/tty_ioctl.h b/na/include/lume/tty_ioctl.h
deleted file mode 100644
index 87ba59b..0000000
--- a/na/include/lume/tty_ioctl.h
+++ /dev/null
@@ -1,202 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * tty_ioctl.h — Terminal I/O control constants and structures
- *
- * Linux-compatible termios, winsize, and TIOC* ioctl definitions.
- * Values match the RISC-V/generic Linux ABI.
- *
- * Reference: include/uapi/asm-generic/termbits.h,
- * include/uapi/asm-generic/ioctls.h
- */
-
-#include <lume/types.h>
-
-/* ================================================================== */
-/* termios c_cc indices */
-/* ================================================================== */
-
-inline constexpr int VINTR = 0;
-inline constexpr int VQUIT = 1;
-inline constexpr int VERASE = 2;
-inline constexpr int VKILL = 3;
-inline constexpr int VEOF = 4;
-inline constexpr int VTIME = 5;
-inline constexpr int VMIN = 6;
-inline constexpr int VSWTC = 7;
-inline constexpr int VSTART = 8;
-inline constexpr int VSTOP = 9;
-inline constexpr int VSUSP = 10;
-inline constexpr int VEOL = 11;
-inline constexpr int VREPRINT = 12;
-inline constexpr int VDISCARD = 13;
-inline constexpr int VWERASE = 14;
-inline constexpr int VLNEXT = 15;
-inline constexpr int VEOL2 = 16;
-inline constexpr int NCCS = 19;
-
-/* ================================================================== */
-/* c_iflag — input mode flags */
-/* ================================================================== */
-
-inline constexpr uint32 IGNBRK = 0000001;
-inline constexpr uint32 BRKINT = 0000002;
-inline constexpr uint32 IGNPAR = 0000004;
-inline constexpr uint32 PARMRK = 0000010;
-inline constexpr uint32 INPCK = 0000020;
-inline constexpr uint32 ISTRIP = 0000040;
-inline constexpr uint32 INLCR = 0000100;
-inline constexpr uint32 IGNCR = 0000200;
-inline constexpr uint32 ICRNL = 0000400;
-inline constexpr uint32 IUCLC = 0001000;
-inline constexpr uint32 IXON = 0002000;
-inline constexpr uint32 IXANY = 0004000;
-inline constexpr uint32 IXOFF = 0010000;
-inline constexpr uint32 IMAXBEL = 0020000;
-inline constexpr uint32 IUTF8 = 0040000;
-
-/* ================================================================== */
-/* c_oflag — output mode flags */
-/* ================================================================== */
-
-inline constexpr uint32 OPOST = 0000001;
-inline constexpr uint32 OLCUC = 0000002;
-inline constexpr uint32 ONLCR = 0000004;
-inline constexpr uint32 OCRNL = 0000010;
-inline constexpr uint32 ONOCR = 0000020;
-inline constexpr uint32 ONLRET = 0000040;
-inline constexpr uint32 OFILL = 0000100;
-inline constexpr uint32 OFDEL = 0000200;
-
-/* ================================================================== */
-/* c_cflag — control mode flags */
-/* ================================================================== */
-
-inline constexpr uint32 CSIZE = 0000060;
-inline constexpr uint32 CS5 = 0000000;
-inline constexpr uint32 CS6 = 0000020;
-inline constexpr uint32 CS7 = 0000040;
-inline constexpr uint32 CS8 = 0000060;
-inline constexpr uint32 CSTOPB = 0000100;
-inline constexpr uint32 CREAD = 0000200;
-inline constexpr uint32 PARENB = 0000400;
-inline constexpr uint32 PARODD = 0001000;
-inline constexpr uint32 HUPCL = 0002000;
-inline constexpr uint32 CLOCAL = 0004000;
-
-/* Baud rates (stored in c_cflag masked with CBAUD) */
-inline constexpr uint32 CBAUD = 0010017;
-inline constexpr uint32 B0 = 0000000;
-inline constexpr uint32 B9600 = 0000015;
-inline constexpr uint32 B19200 = 0000016;
-inline constexpr uint32 B38400 = 0000017;
-inline constexpr uint32 B115200 = 0010002;
-
-/* ================================================================== */
-/* c_lflag — local mode flags */
-/* ================================================================== */
-
-inline constexpr uint32 ISIG = 0000001;
-inline constexpr uint32 ICANON = 0000002;
-inline constexpr uint32 XCASE = 0000004;
-inline constexpr uint32 LECHO = 0000010; /* Named LECHO to avoid macro conflict */
-inline constexpr uint32 ECHOE = 0000020;
-inline constexpr uint32 ECHOK = 0000040;
-inline constexpr uint32 ECHONL = 0000100;
-inline constexpr uint32 NOFLSH = 0000200;
-inline constexpr uint32 TOSTOP = 0000400;
-inline constexpr uint32 ECHOCTL = 0001000;
-inline constexpr uint32 ECHOPRT = 0002000;
-inline constexpr uint32 ECHOKE = 0004000;
-inline constexpr uint32 IEXTEN = 0100000;
-
-/* Linux uses ECHO = 0x0008, we use LECHO to avoid reserved-name issues.
- * Provide the standard name as well for ioctl compatibility. */
-inline constexpr uint32 ECHO = LECHO;
-
-/* ================================================================== */
-/* termios structure */
-/* ================================================================== */
-
-struct termios {
- uint32 c_iflag; /* input mode flags */
- uint32 c_oflag; /* output mode flags */
- uint32 c_cflag; /* control mode flags */
- uint32 c_lflag; /* local mode flags */
- uint8 c_line; /* line discipline */
- uint8 c_cc[NCCS]; /* control characters */
-};
-
-/* ================================================================== */
-/* winsize structure */
-/* ================================================================== */
-
-struct winsize {
- uint16 ws_row;
- uint16 ws_col;
- uint16 ws_xpixel;
- uint16 ws_ypixel;
-};
-
-/* ================================================================== */
-/* ioctl command numbers (Linux asm-generic/ioctls.h) */
-/* ================================================================== */
-
-inline constexpr uint32 TCGETS = 0x5401;
-inline constexpr uint32 TCSETS = 0x5402;
-inline constexpr uint32 TCSETSW = 0x5403;
-inline constexpr uint32 TCSETSF = 0x5404;
-inline constexpr uint32 TCGETA = 0x5405;
-inline constexpr uint32 TCSETA = 0x5406;
-inline constexpr uint32 TCSETAW = 0x5407;
-inline constexpr uint32 TCSETAF = 0x5408;
-inline constexpr uint32 TCSBRK = 0x5409;
-inline constexpr uint32 TCXONC = 0x540A;
-inline constexpr uint32 TCFLSH = 0x540B;
-inline constexpr uint32 TIOCEXCL = 0x540C;
-inline constexpr uint32 TIOCNXCL = 0x540D;
-inline constexpr uint32 TIOCSCTTY = 0x540E;
-inline constexpr uint32 TIOCGPGRP = 0x540F;
-inline constexpr uint32 TIOCSPGRP = 0x5410;
-inline constexpr uint32 TIOCOUTQ = 0x5411;
-inline constexpr uint32 TIOCSTI = 0x5412;
-inline constexpr uint32 TIOCGWINSZ = 0x5413;
-inline constexpr uint32 TIOCSWINSZ = 0x5414;
-inline constexpr uint32 TIOCMGET = 0x5415;
-inline constexpr uint32 TIOCMSET = 0x5418;
-inline constexpr uint32 FIONREAD = 0x541B;
-inline constexpr uint32 TIOCNOTTY = 0x5422;
-inline constexpr uint32 TIOCGPTLCK = 0x80045430;
-inline constexpr uint32 TIOCSPTLCK = 0x40045431;
-
-/* Default termios for a new TTY */
-inline void tty_default_termios(termios* t)
-{
- t->c_iflag = ICRNL | IXON | IUTF8;
- t->c_oflag = OPOST | ONLCR;
- t->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
- t->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
- t->c_line = 0; /* N_TTY */
-
- for (int i = 0; i < NCCS; i++)
- t->c_cc[i] = 0;
-
- t->c_cc[VINTR] = 003; /* Ctrl+C */
- t->c_cc[VQUIT] = 034; /* Ctrl+\ */
- t->c_cc[VERASE] = 0177; /* DEL */
- t->c_cc[VKILL] = 025; /* Ctrl+U */
- t->c_cc[VEOF] = 004; /* Ctrl+D */
- t->c_cc[VSTART] = 021; /* Ctrl+Q */
- t->c_cc[VSTOP] = 023; /* Ctrl+S */
- t->c_cc[VSUSP] = 032; /* Ctrl+Z */
- t->c_cc[VREPRINT] = 022; /* Ctrl+R */
- t->c_cc[VWERASE] = 027; /* Ctrl+W */
- t->c_cc[VLNEXT] = 026; /* Ctrl+V */
- t->c_cc[VEOL] = 0;
- t->c_cc[VEOL2] = 0;
- t->c_cc[VMIN] = 1;
- t->c_cc[VTIME] = 0;
-}
diff --git a/na/include/lume/types.h b/na/include/lume/types.h
deleted file mode 100644
index 2342359..0000000
--- a/na/include/lume/types.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#ifndef _LUME_TYPES_H
-#define _LUME_TYPES_H
-
-/*
- * LumeOS Base Type Definitions
- *
- * Three naming layers coexist so that source files imported from
- * upstream projects (Linux kernel, libfdt) compile unchanged:
- *
- * LumeOS native uint8 / int8 / ... / uint64 / int64
- * Linux kernel short names u8 / s8 / ... / u64 / s64
- * POSIX / C99 (with _t) uint8_t / ... / uint64_t / intptr_t
- *
- * All three resolve to the same underlying compiler builtin, so they
- * are binary-compatible.
- *
- * Declared with C `typedef` (not C++ `using`) so this header may be
- * included from .c sources — required for libfdt shims and the
- * arch/riscv/kernel/pi/ subsystem, which are pure C imports from
- * Linux. C++ accepts `typedef` syntax identically.
- */
-
-/* ===== LumeOS native names ===== */
-typedef unsigned char uint8;
-typedef unsigned short uint16;
-typedef unsigned int uint32;
-typedef unsigned long long uint64;
-
-typedef signed char int8;
-typedef signed short int16;
-typedef signed int int32;
-typedef signed long long int64;
-
-typedef __SIZE_TYPE__ size_t;
-typedef __PTRDIFF_TYPE__ ptrdiff_t;
-typedef int64 ssize_t;
-typedef uint64 uintptr;
-
-/* ===== Linux kernel short names ===== */
-typedef uint8 u8; typedef int8 s8;
-typedef uint16 u16; typedef int16 s16;
-typedef uint32 u32; typedef int32 s32;
-typedef uint64 u64; typedef int64 s64;
-
-/* ===== POSIX / C99 aliases =====
- * Guarded so a later #include <stdint.h> from libc-shim during musl
- * userspace builds is a no-op rather than a redefinition error. */
-#ifndef _STDINT_H
-# define _STDINT_H
-typedef uint8 uint8_t;
-typedef uint16 uint16_t;
-typedef uint32 uint32_t;
-typedef uint64 uint64_t;
-typedef int8 int8_t;
-typedef int16 int16_t;
-typedef int32 int32_t;
-typedef int64 int64_t;
-typedef uintptr uintptr_t;
-typedef int64 intptr_t;
-#endif
-
-/* ===== Linux domain typedefs needed by RISC-V boot path ===== */
-typedef uint64 phys_addr_t;
-
-/* ===== libfdt on-wire types (big-endian). Real byte-swap helpers
- * live in <lume/libfdt_env.h>. ===== */
-typedef uint16 fdt16_t;
-typedef uint32 fdt32_t;
-typedef uint64 fdt64_t;
-
-#ifndef offsetof
-# define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
-#endif
-
-#endif /* _LUME_TYPES_H */
diff --git a/na/include/lume/uaccess.h b/na/include/lume/uaccess.h
deleted file mode 100644
index 5a9ed71..0000000
--- a/na/include/lume/uaccess.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * User-space memory access — safe copy routines.
- *
- * Phase 2: Software page-table validation.
- * Phase 3+: Exception Table (zero-cost happy path).
- *
- * Reference: docs/specs/syscall.md §8
- */
-
-#include <lume/types.h>
-
-/* Copy len bytes from user address to kernel buffer.
- * Returns 0 on success, -EFAULT if user address is invalid. */
-int copy_from_user(void* kernel_dst, uint64 user_src, uint64 len);
-
-/* Copy len bytes from kernel buffer to user address.
- * Returns 0 on success, -EFAULT if user address is invalid. */
-int copy_to_user(uint64 user_dst, const void* kernel_src, uint64 len);
-
-/* Copy NUL-terminated string from user space.
- * Returns string length (excluding NUL) or -EFAULT. */
-int64 strncpy_from_user(char* kernel_dst, uint64 user_src, uint64 max_len);
diff --git a/na/include/lume/uuid.h b/na/include/lume/uuid.h
deleted file mode 100644
index fcbd4a3..0000000
--- a/na/include/lume/uuid.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * UUID/GUID definition
- *
- * Copyright (C) 2010, 2016 Intel Corp.
- * Huang Ying <ying.huang@intel.com>
- */
-
-#pragma once
-
-#include <lume/types.h>
-#include <libc-shim/string.h>
-
-constexpr uint32 UUID_SIZE = 16;
-constexpr uint32 UUID_STRING_LEN = 36;
-
-static inline int uuid_hex_value(char c)
-{
- if (c >= '0' && c <= '9') {
- return c - '0';
- }
- if (c >= 'a' && c <= 'f') {
- return c - 'a' + 10;
- }
- if (c >= 'A' && c <= 'F') {
- return c - 'A' + 10;
- }
- return -1;
-}
-
-static inline bool uuid_parse_raw(const char* uuid, uint8 out[UUID_SIZE])
-{
- if (!uuid) {
- return false;
- }
- if (strlen(uuid) != UUID_STRING_LEN) {
- return false;
- }
- if (uuid[8] != '-' || uuid[13] != '-' || uuid[18] != '-' || uuid[23] != '-') {
- return false;
- }
-
- int out_index = 0;
- for (int i = 0; i < static_cast<int>(UUID_STRING_LEN); ) {
- if (uuid[i] == '-') {
- ++i;
- continue;
- }
-
- int hi = uuid_hex_value(uuid[i]);
- int lo = uuid_hex_value(uuid[i + 1]);
- if (hi < 0 || lo < 0) {
- return false;
- }
-
- out[out_index++] = static_cast<uint8>((hi << 4) | lo);
- i += 2;
- }
-
- return out_index == static_cast<int>(UUID_SIZE);
-}
-
-struct uuid_base {
- uint8 b[UUID_SIZE];
-
- bool is_null() const
- {
- for (uint32 i = 0; i < UUID_SIZE; ++i) {
- if (b[i] != 0) {
- return false;
- }
- }
- return true;
- }
-
- bool operator==(const uuid_base& other) const
- {
- return memcmp(b, other.b, UUID_SIZE) == 0;
- }
-
- bool operator!=(const uuid_base& other) const
- {
- return !(*this == other);
- }
-};
-
-struct uuid_t : public uuid_base {
- explicit uuid_t(const uint8 in_b[UUID_SIZE])
- {
- memcpy(b, in_b, UUID_SIZE);
- }
-
- explicit constexpr uuid_t(uint32 a, uint16 b, uint16 c, uint8 d0, uint8 d1,
- uint8 d2, uint8 d3, uint8 d4, uint8 d5,
- uint8 d6, uint8 d7)
- : uuid_base{{
- static_cast<uint8>((a >> 24) & 0xff),
- static_cast<uint8>((a >> 16) & 0xff),
- static_cast<uint8>((a >> 8) & 0xff),
- static_cast<uint8>(a & 0xff),
- static_cast<uint8>((b >> 8) & 0xff),
- static_cast<uint8>(b & 0xff),
- static_cast<uint8>((c >> 8) & 0xff),
- static_cast<uint8>(c & 0xff),
- d0, d1, d2, d3, d4, d5, d6, d7
- }}
- {
- }
-
- constexpr uuid_t() : uuid_base{{0}} {}
-
- static bool parse(const char* uuid, uuid_t& out)
- {
- uint8 tmp[UUID_SIZE];
- if (!uuid_parse_raw(uuid, tmp)) {
- return false;
- }
-
- memcpy(out.b, tmp, UUID_SIZE);
- return true;
- }
-
- static bool is_valid(const char* uuid)
- {
- uint8 tmp[UUID_SIZE];
- return uuid_parse_raw(uuid, tmp);
- }
-
- static constexpr uuid_t null()
- {
- return uuid_t();
- }
-
- void import_bytes(const uint8* src)
- {
- memcpy(b, src, UUID_SIZE);
- }
-
- void export_bytes(uint8* dst) const
- {
- memcpy(dst, b, UUID_SIZE);
- }
-};
-
-struct guid_t : public uuid_base {
- explicit guid_t(const uint8 in_b[UUID_SIZE])
- {
- memcpy(b, in_b, UUID_SIZE);
- }
-
- explicit constexpr guid_t(uint32 a, uint16 b, uint16 c, uint8 d0, uint8 d1,
- uint8 d2, uint8 d3, uint8 d4, uint8 d5,
- uint8 d6, uint8 d7)
- : uuid_base{{
- static_cast<uint8>(a & 0xff),
- static_cast<uint8>((a >> 8) & 0xff),
- static_cast<uint8>((a >> 16) & 0xff),
- static_cast<uint8>((a >> 24) & 0xff),
- static_cast<uint8>(b & 0xff),
- static_cast<uint8>((b >> 8) & 0xff),
- static_cast<uint8>(c & 0xff),
- static_cast<uint8>((c >> 8) & 0xff),
- d0, d1, d2, d3, d4, d5, d6, d7
- }}
- {
- }
-
- constexpr guid_t() : uuid_base{{0}} {}
- static bool parse(const char* uuid, guid_t& out)
- {
- uint8 tmp[UUID_SIZE];
- if (!uuid_parse_raw(uuid, tmp)) {
- return false;
- }
-
- static constexpr uint8 guid_index[UUID_SIZE] = {
- 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15,
- };
-
- for (int i = 0; i < static_cast<int>(UUID_SIZE); ++i) {
- out.b[guid_index[i]] = tmp[i];
- }
- return true;
- }
-
- static bool is_valid(const char* uuid)
- {
- uint8 tmp[UUID_SIZE];
- return uuid_parse_raw(uuid, tmp);
- }
-
- static constexpr guid_t null()
- {
- return guid_t();
- }
-
- void import_bytes(const uint8* src)
- {
- memcpy(b, src, UUID_SIZE);
- }
-
- void export_bytes(uint8* dst) const
- {
- memcpy(dst, b, UUID_SIZE);
- }
-};
-
diff --git a/na/include/lume/vmm.h b/na/include/lume/vmm.h
deleted file mode 100644
index 9cbebf1..0000000
--- a/na/include/lume/vmm.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * vmm.h — Virtual Memory Manager
- *
- * Managing kernel and user virtual address spaces.
- *
- * Reference: docs/specs/vmm.md
- */
-
-#include <lume/types.h>
-#include <lume/rbtree.h>
-#include <lume/atomic.h>
-#include <kernel/sync/spinlock.h>
-
-/* ------------------------------------------------------------------ */
-/* MI-layer permission flags (architecture-independent) */
-/* */
-/* These are used by VMA and vmm_map_user / vmm_handle_page_fault. */
-/* The pmap MD layer translates them to hardware PTE bits. */
-/* ------------------------------------------------------------------ */
-inline constexpr uint64 VM_READ = (1ULL << 0);
-inline constexpr uint64 VM_WRITE = (1ULL << 1);
-inline constexpr uint64 VM_EXEC = (1ULL << 2);
-inline constexpr uint64 VM_USER = (1ULL << 3);
-
-enum class VmFaultCause : uint8 {
- Read = 0,
- Write = 1,
- Exec = 2,
-};
-
-/* Forward declarations */
-struct VmSpace;
-struct VmAreaStruct;
-
-/*
- * Virtual Memory Area (VMA)
- * Represents a contiguous range [start, end) of uniform permissions.
- */
-struct VmAreaStruct {
- uint64 start; // Start VA (page aligned)
- uint64 end; // End VA (exclusive, page aligned)
- uint64 perm; // VM_READ | VM_WRITE | VM_EXEC | VM_USER
- struct rb_node rb; // Linkage in VmSpace::vma_tree
- VmSpace* mm; // Back pointer to the parent address space
-};
-
-/*
- * Address Space (VmSpace)
- * Represents a user process's complete virtual memory mapping.
- *
- * Lock contract:
- * Holds: lock (internal spinlock)
- * May acquire: PMM global lock (via pmm_alloc_page inside page fault)
- * Callers must NOT hold: PMM lock (lock ordering: VmSpace -> PMM)
- */
-struct VmSpace {
- uint64 root_pa; // SV39 root page table physical address
- struct rb_root vma_tree; // Red-black tree of VmAreaStructs
- Spinlock lock; // Protects vma_tree mutations
- lume::atomic<int32> refcount; // Reference count (for CLONE_VM sharing)
- lume::atomic<uint64> cpu_mask; // Bitmask of CPUs with this mm in satp
-
- uint64 brk_start; // Start of heap (page-aligned)
- uint64 brk_end; // Current heap end (page-aligned)
- uint64 mmap_base; // Base for mmap (Linux: TASK_UNMAPPED_BASE = TASK_SIZE/3)
-
- // Create a brand new VmSpace (allocates root pagetable, refcount=1)
- static VmSpace* create();
-
- // Increment reference count (for CLONE_VM)
- void incref();
-
- // Decrement reference count; destroys VmSpace when it reaches 0.
- // Call this instead of destroy() directly.
- void put();
-
- // Destroy VmSpace, unmapping all VMAs and freeing pagetables.
- // Internal — called by put() when refcount drops to 0.
- void destroy();
-};
-
-/* Initialize kernel page table (Phase 1) */
-void vmm_init();
-
-/* Switch AP to the kernel page table built by BSP (Phase 1) */
-void vmm_init_ap();
-
-/* Map MMIO device region to the active kernel page table */
-void vmm_map_kernel_mmio(uint64 pa, uint64 size);
-
-/* Query page_map allocation (set by vmm_init, read by pmm_init).
- * Returns the PA, byte size, and page count of the vmemmap region. */
-uint64 vmm_get_page_map_pa();
-uint64 vmm_get_page_map_size();
-uint64 vmm_get_num_pages();
-
-/* User space memory management (VMA & Demand Paging) */
-int vmm_map_user(VmSpace* space, uint64 va, uint64 len, uint64 perm);
-int vmm_unmap_user(VmSpace* space, uint64 va, uint64 len);
-int vmm_protect_user(VmSpace* space, uint64 va, uint64 len, uint64 perm);
-
-/* VMA helpers for mmap/mprotect implementations */
-uint64 mm_find_free_va(VmSpace* space, uint64 length, uint64 align);
-bool vma_range_covered(VmSpace* space, uint64 start, uint64 end);
-
-/* Handle user page fault (lookup VMA -> allocate page -> map) */
-int vmm_handle_page_fault(VmSpace* space, uint64 fault_addr, VmFaultCause cause);
-
-/* Clone an address space for fork() */
-VmSpace* vmm_clone(VmSpace* parent_space);
diff --git a/na/include/lume/wait.h b/na/include/lume/wait.h
deleted file mode 100644
index 865c1f2..0000000
--- a/na/include/lume/wait.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * POSIX waitpid / wait4 Constants
- * Reference: docs/specs/syscall.md §9
- */
-
-/* waitpid options */
-inline constexpr int WNOHANG = 1;
-inline constexpr int WUNTRACED = 2;
-inline constexpr int WCONTINUED = 8;
-inline constexpr int WSTOPPED = WUNTRACED; /* POSIX alias */
-inline constexpr int WEXITED = 4; /* POSIX */
-inline constexpr int WNOWAIT = 0x01000000; /* POSIX: peek, don't reap */
-
-/* Exit status encoding (Linux-compatible) */
-static inline int WEXITSTATUS(int s) { return (s >> 8) & 0xFF; }
-static inline bool WIFEXITED(int s) { return (s & 0x7F) == 0; }
-static inline bool WIFSIGNALED(int s) { return (s & 0x7F) != 0 && (s & 0x7F) != 0x7F; }
-static inline int WTERMSIG(int s) { return s & 0x7F; }
-static inline bool WIFSTOPPED(int s) { return (s & 0xFF) == 0x7F; }
-static inline int WSTOPSIG(int s) { return (s >> 8) & 0xFF; }
-static inline bool WIFCONTINUED(int s){ return s == 0xFFFF; }
-
-/* Encode exit code into wstatus (normal exit) */
-static inline int W_EXITCODE(int code) { return (code & 0xFF) << 8; }
-static inline int W_STOPCODE(int sig) { return ((sig & 0xFF) << 8) | 0x7F; }
-static inline int W_CONTINUED() { return 0xFFFF; }
diff --git a/na/include/lume/waitqueue.h b/na/include/lume/waitqueue.h
deleted file mode 100644
index 3720cb7..0000000
--- a/na/include/lume/waitqueue.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * WaitQueue — blocking synchronization primitive
- *
- * Supports two usage patterns:
- *
- * 1. Simple (legacy): wq.sleep() — combines prepare/schedule/finish.
- * Suitable for simple cases where no external condition is checked.
- *
- * 2. Linux-style two-phase (recommended for correctness):
- *
- * wq.prepare_to_wait(curr, TaskState::Sleeping);
- * if (!condition) {
- * schedule();
- * }
- * wq.finish_wait(curr);
- *
- * This pattern prevents the "lost wakeup" race: the task is already
- * on the wait list (and marked Sleeping) BEFORE checking the condition.
- * If a wake_one/wake_all fires between the check and schedule(), the
- * task's state will already be set back to Runnable, and schedule()
- * will see state == Runnable and simply re-run the task.
- *
- * Reference: docs/specs/scheduler.md, docs/specs/task.md §5
- */
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <kernel/sync/spinlock.h>
-
-struct TaskControlBlock;
-enum class TaskState : uint8;
-
-class WaitQueue {
-public:
- void init(const char* name = "wq") {
- head_.init();
- lock_.init(name);
- }
-
- /*
- * Phase 1: Add the task to the wait queue and set its state.
- * The task is NOT yet descheduled — the caller must check the
- * condition and call schedule() if the condition is still false.
- *
- * Must be called with interrupts disabled or preemption disabled.
- */
- void prepare_to_wait(TaskControlBlock* task, TaskState new_state);
-
- /*
- * Phase 3: Remove the task from the wait queue (if still on it)
- * and ensure its state is Runnable. Called after schedule() returns
- * (i.e. after the task has been woken up, or if the condition was
- * true and schedule() was skipped).
- */
- void finish_wait(TaskControlBlock* task);
-
- /* Legacy convenience: combines prepare_to_wait + schedule + finish_wait.
- * If external_lock is provided, it is released AFTER the task is
- * added to the wait queue but BEFORE schedule() is called. */
- void sleep(Spinlock* external_lock = nullptr);
-
- /* Wake the first (oldest) sleeping task. Returns true if a task was woken. */
- bool wake_one();
-
- /* Wake all sleeping tasks. Returns the number of tasks woken. */
- int wake_all();
-
- /* Check if any tasks are waiting. */
- bool is_empty() const { return head_.is_empty(); }
-
-private:
- list_node head_;
- Spinlock lock_;
-};
diff --git a/na/kernel/exec.cc b/na/kernel/exec.cc
deleted file mode 100644
index 616c0da..0000000
--- a/na/kernel/exec.cc
+++ /dev/null
@@ -1,659 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * exec.cc — ELF64 binary loader and execve implementation
- *
- * Loads a static ELF64 executable into a fresh address space,
- * constructs the user stack (argc/argv/envp/auxv), and redirects
- * the current task's trapframe to begin executing at the ELF entry.
- *
- * Reference: Linux fs/binfmt_elf.c, fs/exec.c
- */
-
-#include <lume/elf.h>
-#include <lume/types.h>
-#include <lume/task.h>
-#include <lume/vmm.h>
-#include <lume/addr.h>
-#include <lume/config.h>
-#include <lume/errno.h>
-#include <lume/pmm.h>
-#include <lume/kprintf.h>
-#include <lume/klog.h>
-#include <lume/file.h>
-#include <lume/fs.h>
-#include <lume/dcache.h>
-#include <lume/namei.h>
-#include <lume/fdtable.h>
-#include <lume/mm_layout.h>
-#include <lume/signal_types.h>
-#include <arch/pmap.h>
-#include <arch/trapframe.h>
-#include <arch/mmu.h>
-#include <arch/cpu.h>
-#include <arch/trap.h>
-
-/* Assembly entry points (trap_vector.S) */
-extern "C" void trap_vector();
-extern "C" [[noreturn]] void trap_dispatch(arch::TrapFrame*);
-extern "C" void userret(arch::TrapFrame* tf);
-
-/* ================================================================== */
-/* Constants */
-/* ================================================================== */
-
-/* User stack layout comes from mm_layout.h (MI layer).
- * kUserStackTop, kUserStackBase, kUserStackSize are defined there,
- * derived from kUserVaLimit so they automatically adapt to the
- * page table mode (SV39/SV48). */
-
-/* ================================================================== */
-/* Helper: kernel_read — read from a file into a kernel buffer */
-/* ================================================================== */
-
-static int64 kernel_read(file* fp, void* buf, uint64 count, int64 offset)
-{
- int64 pos = offset;
- if (!fp->f_op || !fp->f_op->read) return -EINVAL;
- return fp->f_op->read(fp, static_cast<char*>(buf), count, &pos);
-}
-
-/* ================================================================== */
-/* Helper: copy data to a user page via kernel direct map */
-/* ================================================================== */
-
-static void memcpy_kernel(void* dst, const void* src, uint64 n)
-{
- auto* d = static_cast<uint8*>(dst);
- auto* s = static_cast<const uint8*>(src);
- for (uint64 i = 0; i < n; i++)
- d[i] = s[i];
-}
-
-static void memset_kernel(void* dst, uint8 val, uint64 n)
-{
- auto* d = static_cast<uint8*>(dst);
- for (uint64 i = 0; i < n; i++)
- d[i] = val;
-}
-
-static uint64 strlen_kernel(const char* s)
-{
- uint64 n = 0;
- while (s[n]) n++;
- return n;
-}
-
-/* ================================================================== */
-/* Helper: allocate and map a page at a specific user VA */
-/* ================================================================== */
-
-static int map_page_at(VmSpace* mm, uint64 va, uint64 pte_perm)
-{
- Page* pg = pmm_alloc_page();
- if (!pg) return -ENOMEM;
-
- uint64 pa = page_to_pa(pg).raw;
-
- /* Zero the page */
- void* kva = reinterpret_cast<void*>(pa_to_va(PhysAddr{pa}).raw);
- memset_kernel(kva, 0, kPageSize);
-
- int ret = pmap::map(mm->root_pa, va, pa, pte_perm);
- if (ret != 0) {
- pmm_free_page(pg);
- return -ENOMEM;
- }
- page_inc_mapcount(pg);
- return 0;
-}
-
-/* ================================================================== */
-/* load_elf_binary — parse ELF and map segments */
-/* ================================================================== */
-
-struct elf_load_info {
- uint64 entry; /* ELF entry point (after base offset) */
- uint64 phdr_addr; /* User VA where phdr was loaded */
- uint16 phnum; /* Number of program headers */
- uint16 phentsize; /* Size of each phdr */
- uint64 brk_start; /* End of highest PT_LOAD segment (for brk) */
- uint64 load_bias; /* PIE load bias (0 for ET_EXEC) */
-};
-
-static int load_elf_binary(file* fp, VmSpace* mm, elf_load_info* info)
-{
- /* Read ELF header */
- Elf64_Ehdr ehdr{};
- int64 n = kernel_read(fp, &ehdr, sizeof(ehdr), 0);
- if (n < static_cast<int64>(sizeof(ehdr))) {
- printk(KERN_ERR "exec: failed to read ELF header (got %lld)\n", n);
- return -ENOEXEC;
- }
-
- /* Validate magic */
- if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
- ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
- ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
- ehdr.e_ident[EI_MAG3] != ELFMAG3) {
- printk(KERN_ERR "exec: bad ELF magic\n");
- return -ENOEXEC;
- }
-
- /* Validate class (64-bit) and endianness (little-endian) */
- if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) return -ENOEXEC;
- if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) return -ENOEXEC;
-
- /* Validate type and machine */
- if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) return -ENOEXEC;
- if (ehdr.e_machine != EM_RISCV) return -ENOEXEC;
-
- if (ehdr.e_phnum == 0 || ehdr.e_phentsize < sizeof(Elf64_Phdr))
- return -ENOEXEC;
-
- /* Read program headers */
- uint64 phdr_total = static_cast<uint64>(ehdr.e_phnum) * ehdr.e_phentsize;
- if (phdr_total > kPageSize) return -ENOEXEC; /* Sanity limit */
-
- /* Allocate kernel buffer for program headers */
- uint8 phdr_buf[512]; /* Max ~8 phdrs * 56 bytes each */
- if (phdr_total > sizeof(phdr_buf)) return -ENOEXEC;
-
- n = kernel_read(fp, phdr_buf, phdr_total, static_cast<int64>(ehdr.e_phoff));
- if (n < static_cast<int64>(phdr_total)) return -ENOEXEC;
-
- info->phnum = ehdr.e_phnum;
- info->phentsize = ehdr.e_phentsize;
- info->phdr_addr = 0;
- info->brk_start = 0;
- info->load_bias = 0;
-
- /*
- * PIE (ET_DYN): segments have p_vaddr starting near 0.
- * We must choose a load base and add it to all addresses.
- * Linux uses ELF_ET_DYN_BASE (~2/3 of TASK_SIZE); we use 0x400000.
- */
- constexpr uint64 kPieLoadBase = 0x400000;
- uint64 load_bias = (ehdr.e_type == ET_DYN) ? kPieLoadBase : 0;
- info->load_bias = load_bias;
- info->entry = ehdr.e_entry + load_bias;
-
- /* Process each PT_LOAD segment */
- for (uint16 i = 0; i < ehdr.e_phnum; i++) {
- auto* phdr = reinterpret_cast<Elf64_Phdr*>(
- phdr_buf + i * ehdr.e_phentsize);
-
- if (phdr->p_type == PT_PHDR) {
- /* Remember where program headers are mapped */
- info->phdr_addr = phdr->p_vaddr + load_bias;
- continue;
- }
-
- if (phdr->p_type != PT_LOAD) continue;
- if (phdr->p_memsz == 0) continue;
-
- /* Compute permissions */
- uint64 perm = pmap::PTE_V | pmap::PTE_U;
- if (phdr->p_flags & PF_R) perm |= pmap::PTE_R;
- if (phdr->p_flags & PF_W) perm |= pmap::PTE_W;
- if (phdr->p_flags & PF_X) perm |= pmap::PTE_X;
-
- /* Page-align the segment boundaries (apply load bias for PIE) */
- uint64 seg_start = page_align_down(phdr->p_vaddr + load_bias);
- uint64 seg_end = page_align_up(phdr->p_vaddr + phdr->p_memsz + load_bias);
-
- /* Create VMA for this segment */
- uint64 vm_perm = VM_USER;
- if (phdr->p_flags & PF_R) vm_perm |= VM_READ;
- if (phdr->p_flags & PF_W) vm_perm |= VM_WRITE;
- if (phdr->p_flags & PF_X) vm_perm |= VM_EXEC;
-
- int err = vmm_map_user(mm, seg_start, seg_end - seg_start, vm_perm);
- if (err) {
- printk(KERN_ERR "exec: vmm_map_user failed for segment %d: %d\n",
- i, err);
- return err;
- }
-
- /* Map and fill pages */
- for (uint64 va = seg_start; va < seg_end; va += kPageSize) {
- err = map_page_at(mm, va, perm);
- if (err) return err;
-
- /* Determine how much file data goes into this page */
- uint64 file_seg_start = phdr->p_vaddr + load_bias;
- uint64 file_seg_end = phdr->p_vaddr + phdr->p_filesz + load_bias;
-
- uint64 page_start = va;
- uint64 page_end = va + kPageSize;
-
- /* Overlap between [page_start, page_end) and [file_seg_start, file_seg_end) */
- if (page_start < file_seg_end && page_end > file_seg_start) {
- uint64 copy_start = (page_start > file_seg_start) ? page_start : file_seg_start;
- uint64 copy_end = (page_end < file_seg_end) ? page_end : file_seg_end;
- uint64 copy_len = copy_end - copy_start;
-
- /* File offset for this data */
- uint64 file_off = phdr->p_offset + (copy_start - phdr->p_vaddr - load_bias);
-
- /* Get kernel VA of the mapped page */
- uint64 pa;
- if (!pmap::lookup(mm->root_pa, va, &pa)) return -EFAULT;
- uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
-
- /* Read file data into the page */
- uint64 page_offset = copy_start - va;
- n = kernel_read(fp, kva + page_offset, copy_len,
- static_cast<int64>(file_off));
- if (n < static_cast<int64>(copy_len)) {
- printk(KERN_ERR "exec: short read at offset %llu\n", file_off);
- return -EIO;
- }
- }
- /* BSS region (memsz > filesz) is already zeroed by map_page_at */
- }
-
- /* Track highest loaded address for brk */
- if (seg_end > info->brk_start)
- info->brk_start = seg_end;
-
- /* Detect which PT_LOAD contains the program header table.
- * Linux: fs/binfmt_elf.c line ~1209 */
- if (info->phdr_addr == 0 &&
- phdr->p_offset <= ehdr.e_phoff &&
- ehdr.e_phoff < phdr->p_offset + phdr->p_filesz) {
- info->phdr_addr = ehdr.e_phoff - phdr->p_offset
- + phdr->p_vaddr + load_bias;
- }
- }
-
- /* phdr_addr should have been set by the PT_LOAD loop above
- * or by a PT_PHDR entry. If still zero, something is wrong. */
- if (info->phdr_addr == 0) {
- printk(KERN_ERR "exec: unable to determine AT_PHDR address\n");
- return -ENOEXEC;
- }
-
- return 0;
-}
-
-/* ================================================================== */
-/* setup_user_stack — build argc/argv/envp/auxv on the user stack */
-/* ================================================================== */
-
-static int setup_user_stack(VmSpace* mm, const char* const* argv,
- const char* const* envp,
- const elf_load_info* info,
- uint64* out_sp)
-{
- /* Create stack VMA */
- uint64 vm_perm = VM_USER | VM_READ | VM_WRITE;
- int err = vmm_map_user(mm, kUserStackBase, kUserStackSize, vm_perm);
- if (err) return err;
-
- /* We only need the top few pages for initial stack content.
- * Map 2 pages at the top for argc/argv/envp/auxv. */
- uint64 perm = pmap::PTE_V | pmap::PTE_U | pmap::PTE_R | pmap::PTE_W;
- for (uint64 va = kUserStackTop - 2 * kPageSize; va < kUserStackTop; va += kPageSize) {
- err = map_page_at(mm, va, perm);
- if (err) return err;
- }
-
- /* Count arguments and environment */
- int argc = 0;
- int envc = 0;
- if (argv) { while (argv[argc]) argc++; }
- if (envp) { while (envp[envc]) envc++; }
-
- /*
- * Build the stack from top down using kernel VA access.
- * Layout (top to bottom):
- * 1. String data (argv strings, envp strings)
- * 2. Padding (16-byte align)
- * 3. auxv[]
- * 4. NULL (end of envp)
- * 5. envp[] pointers
- * 6. NULL (end of argv)
- * 7. argv[] pointers
- * 8. argc
- */
-
- /* First pass: calculate total string space */
- uint64 str_total = 0;
- for (int i = 0; i < argc; i++)
- str_total += strlen_kernel(argv[i]) + 1;
- for (int i = 0; i < envc; i++)
- str_total += strlen_kernel(envp[i]) + 1;
-
- /* String area starts at top of stack */
- uint64 str_pos = kUserStackTop - str_total;
- str_pos &= ~15ULL; /* 16-byte align */
-
- /* Copy strings and record user addresses */
- uint64 argv_addrs[32]; /* Max 32 args */
- uint64 envp_addrs[32];
- if (argc > 32) argc = 32;
- if (envc > 32) envc = 32;
-
- uint64 cur = str_pos;
- for (int i = 0; i < argc; i++) {
- uint64 len = strlen_kernel(argv[i]) + 1;
- argv_addrs[i] = cur;
-
- /* Copy string bytes to user stack via kernel VA */
- uint64 pa;
- if (!pmap::lookup(mm->root_pa, page_align_down(cur), &pa))
- return -EFAULT;
- uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
- uint64 off = cur - page_align_down(cur);
- memcpy_kernel(kva + off, argv[i], len);
- cur += len;
- }
- for (int i = 0; i < envc; i++) {
- uint64 len = strlen_kernel(envp[i]) + 1;
- envp_addrs[i] = cur;
-
- uint64 pa;
- if (!pmap::lookup(mm->root_pa, page_align_down(cur), &pa))
- return -EFAULT;
- uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
- uint64 off = cur - page_align_down(cur);
- memcpy_kernel(kva + off, envp[i], len);
- cur += len;
- }
-
- /* Build auxv, envp[], argv[], argc below the strings */
- /* Count entries: argc + argv_ptrs + NULL + envp_ptrs + NULL + auxv (6 pairs + terminator) */
- int auxv_count = 8; /* AT_PHDR, AT_PHENT, AT_PHNUM, AT_PAGESZ, AT_BASE, AT_ENTRY, AT_RANDOM, AT_NULL */
- uint64 table_size = sizeof(uint64) * (1 + argc + 1 + envc + 1 + auxv_count * 2);
- uint64 table_start = str_pos - table_size;
- table_start &= ~15ULL;
-
- /* Write the table entries.
- * We need to handle page boundaries, but for simplicity we assume
- * the table fits in the already-mapped pages. */
- uint64 sp = table_start;
-
- /* Helper: write a uint64 to user stack */
- auto write_u64 = [&](uint64 addr, uint64 val) -> int {
- uint64 pa;
- if (!pmap::lookup(mm->root_pa, page_align_down(addr), &pa))
- return -EFAULT;
- uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
- uint64 off = addr - page_align_down(addr);
- *reinterpret_cast<uint64*>(kva + off) = val;
- return 0;
- };
-
- /* argc */
- err = write_u64(sp, static_cast<uint64>(argc));
- if (err) return err;
- sp += 8;
-
- /* argv[] */
- for (int i = 0; i < argc; i++) {
- err = write_u64(sp, argv_addrs[i]);
- if (err) return err;
- sp += 8;
- }
- err = write_u64(sp, 0); /* NULL terminator */
- if (err) return err;
- sp += 8;
-
- /* envp[] */
- for (int i = 0; i < envc; i++) {
- err = write_u64(sp, envp_addrs[i]);
- if (err) return err;
- sp += 8;
- }
- err = write_u64(sp, 0); /* NULL terminator */
- if (err) return err;
- sp += 8;
-
- /* auxv[] */
- auto write_auxv = [&](uint64 type, uint64 val) -> int {
- int r = write_u64(sp, type);
- if (r) return r;
- sp += 8;
- r = write_u64(sp, val);
- if (r) return r;
- sp += 8;
- return 0;
- };
-
- write_auxv(AT_PHDR, info->phdr_addr);
- write_auxv(AT_PHENT, info->phentsize);
- write_auxv(AT_PHNUM, info->phnum);
- write_auxv(AT_PAGESZ, kPageSize);
- write_auxv(AT_BASE, info->load_bias);
- write_auxv(AT_ENTRY, info->entry);
- write_auxv(AT_RANDOM, str_pos - 16); /* Point to 16 bytes of zeros for now */
- write_auxv(AT_NULL, 0);
-
- *out_sp = table_start;
- return 0;
-}
-
-/* ================================================================== */
-/* do_execve — main entry point */
-/* ================================================================== */
-
-int do_execve(const char* pathname, const char* const* argv,
- const char* const* envp)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) return -EINVAL;
-
- /* 1. Look up the file */
- path p{};
- int err = filename_lookup(AT_FDCWD, pathname, 0, &p);
- if (err) {
- printk(KERN_ERR "exec: file not found: %s (err=%d)\n", pathname, err);
- return err;
- }
-
- inode* ip = p.de->d_inode;
- if (!ip || !S_ISREG(ip->i_mode)) {
- printk(KERN_ERR "exec: not a regular file: %s\n", pathname);
- return -EACCES;
- }
-
- /* Open a kernel file handle */
- file* fp = alloc_empty_file(O_RDONLY);
- if (!fp) {
- return -ENOMEM;
- }
- fp->f_inode = ip;
- fp->f_path = p;
- fp->f_op = ip->i_fop;
- fp->f_pos = 0;
-
- if (fp->f_op && fp->f_op->open) {
- err = fp->f_op->open(ip, fp);
- if (err) {
- fput(fp);
- return err;
- }
- }
-
- /* 2. Create new address space */
- VmSpace* new_mm = VmSpace::create();
- if (!new_mm) {
- fput(fp);
- return -ENOMEM;
- }
-
- /* 3. Load ELF segments */
- elf_load_info info{};
- err = load_elf_binary(fp, new_mm, &info);
- if (err) {
- printk(KERN_ERR "exec: load_elf_binary failed: %d\n", err);
- new_mm->put();
- fput(fp);
- dput(p.de);
- return err;
- }
-
- /* 4. Set up user stack */
- uint64 user_sp = 0;
- err = setup_user_stack(new_mm, argv, envp, &info, &user_sp);
- if (err) {
- printk(KERN_ERR "exec: setup_user_stack failed: %d\n", err);
- new_mm->put();
- fput(fp);
- return err;
- }
-
- /* Set brk */
- new_mm->brk_start = page_align_up(info.brk_start);
- new_mm->brk_end = new_mm->brk_start;
-
- /* Set mmap base — Linux: arch_pick_mmap_layout() sets mm->mmap_base
- * to TASK_UNMAPPED_BASE = PAGE_ALIGN(TASK_SIZE / 3).
- * This ensures mmap(NULL) returns addresses well above the ELF
- * text/data segments. */
- new_mm->mmap_base = page_align_up(kUserVaLimit / 3);
-
- fput(fp);
-
- /* =============================================================== */
- /* Point of no return — replace the current process */
- /* =============================================================== */
-
- /* 5a. Replace address space */
- VmSpace* old_mm = curr->mm;
- curr->mm = new_mm;
- pmap::activate(new_mm->root_pa);
- if (old_mm) old_mm->put();
-
- /* 5a'. Transition from kernel thread to user process.
- * Linux: begin_new_exec() in fs/exec.c:
- * current->flags &= ~PF_KTHREAD; (line 1173)
- * tsk->exit_signal = SIGCHLD; (line 1021)
- * Without this, a kernel_execve'd task stays marked as a kthread
- * and would be auto-reaped (exit_signal=-1) instead of becoming
- * a proper zombie for parent wait(). */
- curr->flags &= ~TF_KTHREAD;
- curr->exit_signal = SIGCHLD;
-
- /* 5b. Reset signal handlers to SIG_DFL (POSIX requirement).
- *
- * Linux fs/exec.c:flush_signal_handlers() runs under sighand->siglock.
- * Even though after fork our sighand is no longer shared with the
- * parent (we copy in task_clone), Linux still locks here — and so
- * must we, because the same sighand may be shared with CLONE_SIGHAND
- * sibling threads of the execing task. Holding siglock here also
- * keeps the per-task blocked mask reset atomic w.r.t. concurrent
- * sigprocmask / signal delivery on this very task. */
- if (curr->sighand) {
- LockGuard guard(curr->sighand->siglock);
- for (int i = 0; i < _NSIG; i++) {
- if (curr->sighand->action[i].sa_handler != SIG_IGN)
- curr->sighand->action[i].sa_handler = SIG_DFL;
- curr->sighand->action[i].sa_flags = 0;
- curr->sighand->action[i].sa_mask.clear();
- }
- curr->blocked.clear();
- } else {
- curr->blocked.clear();
- }
-
- /* 5c. Close O_CLOEXEC file descriptors */
- /* TODO: implement close-on-exec */
-
- /* 6. Set up trapframe to enter user space */
- arch::TrapFrame* tf = curr->trapframe;
- if (!tf) {
- printk(KERN_ERR "exec: no trapframe!\n");
- task_kill_current(SIGKILL);
- return -EINVAL; /* not reached */
- }
-
- /* Initialize trapframe for user-mode return.
- * This is the MI equivalent of Linux's start_thread() + kernel
- * metadata setup. All arch-specific details (sstatus, FP state,
- * kernel_satp, kernel_sp, trap handler address, etc.) are handled
- * by the MD layer. MI exec never touches arch registers. */
- arch::trapframe_init_user(tf, info.entry, user_sp);
-
- pr_debug_s(KLOG_SUBSYS_EXEC,
- "exec: pid %d entry=%llx sp=%llx phdr=%llx\n",
- curr->pid, info.entry, user_sp, info.phdr_addr);
-
- /* Verify loaded data at entry point — debug-level, ring buffer only. */
- {
- uint64 pa;
- if (pmap::lookup(curr->mm->root_pa, page_align_down(info.entry), &pa)) {
- uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
- uint64 off = info.entry - page_align_down(info.entry);
- pr_debug_s(KLOG_SUBSYS_EXEC,
- "exec: entry bytes @%llx: %02x %02x %02x %02x %02x %02x %02x %02x\n",
- info.entry,
- kva[off+0], kva[off+1], kva[off+2], kva[off+3],
- kva[off+4], kva[off+5], kva[off+6], kva[off+7]);
- }
- for (uint64 va = 0x10000; va < 0x14000; va += kPageSize) {
- if (pmap::lookup(curr->mm->root_pa, va, &pa)) {
- uint8* kva2 = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
- pr_debug_s(KLOG_SUBSYS_EXEC,
- "exec: page %llx: %02x%02x%02x%02x %02x%02x%02x%02x\n",
- va, kva2[0], kva2[1], kva2[2], kva2[3],
- kva2[4], kva2[5], kva2[6], kva2[7]);
- } else {
- pr_debug_s(KLOG_SUBSYS_EXEC,
- "exec: page %llx: NOT MAPPED\n", va);
- }
- }
- }
-
- /* Flush instruction cache after loading executable code into memory.
- * Without this, the I-cache may contain stale data for the pages
- * we just loaded, causing the CPU to execute garbage. */
- arch::mmu::flush_icache();
-
- return 0; /* Will return to user mode via trap return */
-}
-
-/* ================================================================== */
-/* sys_execve_wrapper — syscall entry point */
-/* ================================================================== */
-
-int64 sys_execve_wrapper(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- const char* pathname = reinterpret_cast<const char*>(a0);
- const char* const* argv = reinterpret_cast<const char* const*>(a1);
- const char* const* envp = reinterpret_cast<const char* const*>(a2);
-
- return do_execve(pathname, argv, envp);
-}
-
-/* ================================================================== */
-/* kernel_execve — call execve from kernel context (e.g. init) */
-/* ================================================================== */
-
-int kernel_execve(const char* pathname, const char* const* argv,
- const char* const* envp)
-{
- TaskControlBlock* curr = current_task();
-
- int ret = do_execve(pathname, argv, envp);
- if (ret != 0)
- return ret;
-
- /*
- * do_execve succeeded: the trapframe is set up with entry point,
- * user stack, and sstatus (SPP=0 for U-mode). We must NOT return
- * normally — that would go back through the kthread return path
- * and jump to address 0. Instead, jump directly to user mode via
- * the same userret() path that fork_child_entry uses.
- *
- * Switch stvec from kernelvec to trap_vector so that traps from
- * U-mode are handled by the user trap entry path (which loads
- * kernel context from the trapframe via sscratch).
- */
- arch::trap::set_vector(reinterpret_cast<uint64>(trap_vector));
- userret(curr->trapframe);
- __builtin_unreachable();
-}
diff --git a/na/kernel/irq.cc b/na/kernel/irq.cc
deleted file mode 100644
index 4de468a..0000000
--- a/na/kernel/irq.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * IRQ Manager Implementation
- *
- * Lock-free dispatch path for interrupt context safety.
- * Registration/unregistration is protected by atomic publish.
- *
- * Reference: docs/specs/trap.md §3.3
- */
-
-#include <lume/console.h>
-#include <lume/errno.h>
-#include <lume/irq.h>
-
-/* Global singleton */
-IrqManager g_irq_manager;
-
-void IrqManager::init() {
- for (uint32 i = 0; i < kMaxIrqs; i++) {
- entries_[i].handler_ptr.store(0, __ATOMIC_RELAXED);
- entries_[i].arg = nullptr;
- }
-}
-
-int IrqManager::register_handler(uint32 irq, IrqHandler handler, void *arg) {
- if (irq >= kMaxIrqs)
- return -EINVAL;
-
- if (!handler)
- return -EINVAL;
-
- /* Check if already registered (CAS from 0 to handler) */
- uint64 expected = 0;
- uint64 desired = reinterpret_cast<uint64>(handler);
-
- /* Set arg first (before publishing handler) so dispatch sees valid arg */
- entries_[irq].arg = arg;
-
- if (!entries_[irq].handler_ptr.compare_exchange(expected, desired,
- __ATOMIC_ACQ_REL)) {
- /* Already registered by someone else */
- return -EBUSY;
- }
-
- return 0;
-}
-
-void IrqManager::unregister_handler(uint32 irq) {
- if (irq >= kMaxIrqs)
- return;
-
- /* Clear handler with release semantics */
- entries_[irq].handler_ptr.store(0, __ATOMIC_RELEASE);
- entries_[irq].arg = nullptr;
-}
-
-void IrqManager::dispatch(uint32 irq) {
- if (irq >= kMaxIrqs)
- return;
-
- /* Load handler with acquire semantics — pairs with register's release */
- uint64 h = entries_[irq].handler_ptr.load(__ATOMIC_ACQUIRE);
- if (h == 0) {
- /* Spurious or unregistered IRQ — silently ignore */
- return;
- }
-
- auto handler = reinterpret_cast<IrqHandler>(h);
- handler(irq, entries_[irq].arg);
-}
-
-bool IrqManager::has_handler(uint32 irq) const {
- if (irq >= kMaxIrqs)
- return false;
- return entries_[irq].handler_ptr.load(__ATOMIC_ACQUIRE) != 0;
-}
-
-void IrqManager::set_chip(const IrqChip* chip) {
- chip_ = chip;
-}
-
-uint32 IrqManager::claim() const {
- return chip_ ? chip_->claim() : 0;
-}
-
-void IrqManager::complete(uint32 irq) const {
- if (chip_) chip_->complete(irq);
-}
-
-void IrqManager::init_ap() const {
- if (chip_ && chip_->init_ap) chip_->init_ap();
-}
-
-void IrqManager::enable(uint32 irq) const {
- if (chip_ && chip_->enable) chip_->enable(irq);
-}
-
-void IrqManager::disable(uint32 irq) const {
- if (chip_ && chip_->disable) chip_->disable(irq);
-}
-
-void IrqManager::set_priority(uint32 irq, uint32 priority) const {
- if (chip_ && chip_->set_priority) chip_->set_priority(irq, priority);
-}
diff --git a/na/kernel/main.cc b/na/kernel/main.cc
deleted file mode 100644
index db5843c..0000000
--- a/na/kernel/main.cc
+++ /dev/null
@@ -1,468 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * LumeOS Kernel Main Entry — Global Initialization Sequence
- *
- * All CPUs enter kernel_main() after the assembly bootstrap (entry.S)
- * has enabled MMU, cleared BSS, and called early_cpp_init().
- *
- * BSP (cpu_id == 0) executes the full subsystem initialization DAG.
- * APs spin on ap_boot_sync, then perform per-CPU init only.
- *
- * Boot DAG invariants (docs/specs/linux_abi_mm_plan.md §Boot Sequence):
- * - trap_init() MUST precede any allocator (exception safety net)
- * - sched_init() MUST precede timer_init() (software before hardware)
- * - PID 1 (init) is spawned before entering idle loop
- *
- * Reference: docs/specs/global_init.md
- */
-
-#include <arch/config.h>
-#include <arch/cpu.h>
-#include <arch/extable.h>
-#include <arch/sbi.h>
-#include <lume/addr.h>
-#include <lume/atomic.h>
-#include <lume/console.h>
-#include <lume/driver.h>
-#include <lume/fdt.h>
-#include <lume/fdtable.h>
-#include <lume/fs_struct.h>
-#include <lume/fs_types.h>
-#include <lume/irq.h>
-#include <lume/kernel_map.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/memblock.h>
-#include <lume/mount.h>
-#include <lume/page_cache.h>
-#include <lume/pmm.h>
-#include <lume/random.h>
-#include <lume/sched.h>
-#include <lume/smp.h>
-#include <lume/task.h>
-#include <lume/pid.h>
-#include <lume/pgrp.h>
-#include <lume/signal.h>
-#include <lume/signal_types.h>
-#include <lume/trap.h>
-#include <lume/types.h>
-
-// Declared by entry.S: the hart ID that won BSP election
-extern "C" uint32 bsp_hart_id;
-
-// Linker symbol: kernel entry point (physical start address)
-extern "C" char _start[];
-
-// ============================================================
-// Forward declarations for subsystem init functions.
-// Each is implemented by its owning module. On failure they
-// call kernel_panic() directly — no error return.
-// ============================================================
-
-// BSP global init (strict order)
-void console_init(); // 1. UART ready; kprintf/panic can output
-void fdt_init(uint64 fdt_paddr); // 2. Parse FDT, build device node table
-void pmm_init(); // 3. Buddy allocator ready
-void slab_init(); // 4. kmem_cache ready; operator new usable
-void vmm_init(); // 5. Fine-grained kernel page table; retire early_pgdir
-void task_init(); // 6. PID allocator + global task list
-void pgrp_init(); // 6.5. Process group hash table
-void sched_init(); // 7. Create BSP Idle Task, init ready queues
-void timer_init(); // 8. Configure timer interrupt (scheduler tick)
-void syscall_init(); // 9. Register syscall jump table
-void super_init(); // 10. VFS superblock registry
-void ramfs_init(); // 11. RamFS filesystem registration
-void vfs_init(); // 12. Mount root filesystem
-void cdev_init(); // 13. Character device registry
-void console_cdev_init(); // 14. Register console/null/zero cdevs
-void devfs_init(); // 15. Create /dev and populate nodes
-void tty_init(); // 15.5. TTY subsystem (console TTY + UART RX IRQ)
-void populate_rootfs(); // 15.6. Unpack embedded initramfs CPIO into ramfs
-
-// Boot self-test runner
-void selftest_run_all();
-
-// AP per-CPU init
-void vmm_init_ap(); // Switch to BSP-built kernel page table
-void timer_init_ap(); // Start this CPU's timer interrupt
-void sched_init_ap(); // Init per-CPU ready queue, create Idle Task
-
-// kthread_create from task.cc
-extern TaskControlBlock *kthread_create(const char *name, void (*entry)(void *),
- void *arg, uint8 priority);
-
-// ============================================================
-// PID 1 — init thread
-// ============================================================
-static void init_thread_func(void *) {
- printk(KERN_INFO "[init] PID 1 started\n");
-
- /* Run post-SMP tests — APs are online by now */
- extern void selftest_smp();
- kprintf("\n========================================\n");
- kprintf(" Post-SMP Test Suite\n");
- kprintf("========================================\n");
- selftest_smp();
- kprintf("========================================\n");
- kprintf(" Post-SMP tests PASSED\n");
- kprintf("========================================\n\n");
-
- /*
- * Set up init's own VFS context (like Linux kernel_init).
- * kthread_create does NOT inherit files/fs, so init creates its own.
- */
- TaskControlBlock *self = current_task();
-
- if (!self->files) {
- self->files = alloc_files_struct();
- if (!self->files) {
- printk(KERN_ERR "[init] failed to allocate files_struct\n");
- goto out;
- }
- }
- if (!self->fs) {
- self->fs = alloc_fs_struct();
- if (!self->fs) {
- printk(KERN_ERR "[init] failed to allocate fs_struct\n");
- goto out;
- }
- /* Point to the global root filesystem */
- self->fs->root.de = g_root_dentry;
- self->fs->root.mnt = g_root_mnt;
- self->fs->pwd = self->fs->root;
- }
-
- /* Open /dev/console → fd 0 (stdin), dup to fd 1, 2 */
- {
- extern int64 sys_openat(uint64, uint64, uint64, uint64, uint64, uint64);
- extern int64 sys_dup(uint64, uint64, uint64, uint64, uint64, uint64);
-
- int64 fd0 =
- sys_openat(static_cast<uint64>(-100), /* AT_FDCWD */
- reinterpret_cast<uint64>("/dev/console"), O_RDWR, 0, 0, 0);
- if (fd0 < 0) {
- printk(KERN_ERR "[init] open /dev/console failed: %d\n",
- static_cast<int>(fd0));
- } else {
- sys_dup(static_cast<uint64>(fd0), 0, 0, 0, 0, 0); /* fd 1 */
- sys_dup(static_cast<uint64>(fd0), 0, 0, 0, 0, 0); /* fd 2 */
- printk(KERN_INFO "[init] fd 0/1/2 -> /dev/console\n");
- }
- }
-
- /*
- * Try to exec /sbin/init (like Linux kernel_init).
- * If not found, fall through to idle.
- */
- {
- extern int kernel_execve(const char *, const char *const *,
- const char *const *);
-
- const char *init_paths[] = {"/sbin/init", "/etc/init", "/bin/init",
- "/bin/sh", nullptr};
- const char *argv[] = {"/sbin/init", nullptr};
- const char *envp[] = {"HOME=/", "PATH=/sbin:/bin", nullptr};
-
- for (int i = 0; init_paths[i]; i++) {
- argv[0] = init_paths[i];
- int ret = kernel_execve(init_paths[i], argv, envp);
- if (ret == 0) {
- /* Success — do_execve modified our trapframe.
- * When this kthread returns through the trap return path,
- * it will jump to user space. */
- return;
- }
- printk(KERN_INFO "[init] exec %s failed: %d\n", init_paths[i], ret);
- }
-
- printk(KERN_ERR "[init] No init found! Kernel panic.\n");
- }
-
-out:
- extern void task_exit(int code);
- task_exit(0);
-}
-
-// ============================================================
-// Second-stage synchronization barrier (C++ layer)
-// AP cores spin here until BSP finishes all global init.
-// ============================================================
-static lume::atomic<uint32> ap_boot_sync;
-
-// ============================================================
-// kernel_main — C++ entry point for all CPUs
-// ============================================================
-extern "C" [[noreturn]] void kernel_main(uint64 cpu_id, uint64 fdt_paddr) {
-
- if (cpu_id == bsp_hart_id) {
- // ========================================================
- // BSP: Absolute single-core period — no locks needed.
- // APs are spinning on ap_boot_sync.
- // ========================================================
-
- uint64 fdt_pa = fdt_paddr;
-
- // ---- Phase 1: Console + Exception Safety Net ----
-
- // 1. Early console setup: discover UART via FDT before ANY output.
- console_early_init(fdt_pa);
- klog_init();
-
- printk("\n");
- printk(KERN_INFO "LumeOS Kernel v0.1\n");
- printk(KERN_INFO "boot: BSP online, cpu_id=%llu\n", cpu_id);
- printk(KERN_INFO "boot: higher-half kernel active\n");
-
- /* kernel_map filled by arch/riscv/mm/init.cc::setup_vm before MMU
- * enable; relocate_kernel has already applied .rela.dyn. */
- printk(KERN_INFO "boot: kimage phys=0x%llx virt=0x%llx size=%llu\n",
- static_cast<unsigned long long>(kernel_map.phys_addr),
- static_cast<unsigned long long>(kernel_map.virt_addr),
- static_cast<unsigned long long>(kernel_map.size));
-
- // --- Subsystem init DAG (strict top-down order) ---
-
- // 2. Trap handler — install exception vector FIRST.
- // Any fault from here on gets a proper panic backtrace
- // instead of a silent hang. (Linux: trap_init is in start_kernel
- // right after setup_arch.)
- trap_init();
- printk(KERN_INFO "init: trap_init done\n");
-
- // 3. FDT parsing — make device tree available
- fdt_init(fdt_pa);
- printk(KERN_INFO "init: fdt_init done\n");
-
- // 4. KASLR — seed PRNG and randomise VA layout.
- // MUST happen before vmm_init() builds the kernel page table.
- {
- uint64 kaslr_seed = 0;
- fdt_early_get_kaslr_seed(&kaslr_seed);
- random_early_init(kaslr_seed);
- arch::kaslr_init();
- }
-
- // Sort exception table before setting RO permissions in vmm_init()
- extable_init();
-
- // ---- Phase 2: Memory Subsystem (strict cascade) ----
-
- // 5. Memblock allocator (Early boot PA allocator)
- memblock_init(fdt_pa);
- printk(KERN_INFO "init: memblock_init done\n");
-
- // 6. Virtual memory manager — builds page table with KASLR'd bases
- vmm_init();
- printk(KERN_INFO "init: vmm_init done\n");
-
- // 7. Physical memory manager (Buddy)
- pmm_init();
- printk(KERN_INFO "init: pmm_init done\n");
-
- // 8. Slab allocator — operator new usable after this
- slab_init();
- printk(KERN_INFO "init: slab_init done\n");
-
- // 9. Page cache (minimal hash table, stable interface for file-backed mmap)
- page_cache_init();
-
- // 10. FDT Phase 2 — build DeviceNode object tree (needs Slab)
- fdt_unflatten();
- printk(KERN_INFO "init: fdt_unflatten done\n");
-
- // ---- Phase 3: Task & Scheduler (software state before hardware) ----
-
- // 11. Task management (PID allocator + global task list)
- task_init();
- printk(KERN_INFO "init: task_init done\n");
-
- pgrp_init();
-
- sched_init();
- printk(KERN_INFO "init: sched_init done\n");
-
- // ---- Phase 3.5: VFS (needs slab + sched) ----
- super_init();
- ramfs_init();
- vfs_init();
- printk(KERN_INFO "init: vfs_init done (ramfs root mounted)\n");
-
- // ---- Phase 3.55: Unpack initramfs into root ----
- populate_rootfs();
-
- // ---- Phase 3.6: Device nodes ----
- cdev_init();
- console_cdev_init();
- devfs_init();
- printk(KERN_INFO "init: devfs done\n");
-
- // ---- Phase 4: Hardware Drivers & Interrupts ----
-
- // 13. Hardware Drivers Discovery (Interrupt Controllers, etc)
- driver_probe_all();
- printk(KERN_INFO "init: driver_probe_all done\n");
-
- // 13.5. TTY subsystem — needs PLIC (driver_probe_all) for UART RX IRQ
- tty_init();
- printk(KERN_INFO "init: tty_init done\n");
-
- // 14. Timer interrupt (scheduler tick source)
- // Safe now: scheduler is fully initialized.
- timer_init();
- printk(KERN_INFO "init: timer_init done\n");
-
- // 15. Syscall jump table
- syscall_init();
- printk(KERN_INFO "init: syscall_init done\n");
-
- // 16. SMP / IPI infrastructure
- smp_init();
- arch::cpu::software_intr_on(); // Enable S-mode software interrupts for IPI
- arch::cpu::external_intr_on(); // Enable S-mode external interrupts for PLIC (UART)
-
- // ---- Phase 5: Self-test & Launch ----
-
- // 17. Boot self-tests — verify subsystem correctness.
- // Tests that create kthreads must wait() for them (freeing PIDs).
- selftest_run_all();
-
- // 18. Spawn PID 1 (init) — the first persistent task.
- // Like Linux's rest_init() → user_mode_thread(kernel_init).
- // All test PIDs have been freed, so PID 1 is available.
- TaskControlBlock *init_task =
- kthread_create("init", init_thread_func, nullptr, 0);
- if (init_task) {
- init_task->flags |= TF_INIT;
-
- /* Init is session and process-group leader: sid = pgid = pid.
- * Allocate signal_struct AND sighand_struct eagerly so every
- * descendant (created via task_clone) inherits a real identity
- * and a real lock object — signal sending must take siglock and
- * cannot tolerate a null sighand. */
- init_task->signal = alloc_signal_struct(init_task->pid, init_task);
- if (!init_task->signal) {
- printk(KERN_ERR "boot: FAILED to allocate init signal_struct\n");
- }
- init_task->sighand = alloc_sighand();
- if (!init_task->sighand) {
- printk(KERN_ERR "boot: FAILED to allocate init sighand\n");
- }
-
- /* Publish g_init_task so reparenting paths can find the foster
- * parent when other tasks exit before init takes over user space. */
- extern TaskControlBlock* g_init_task;
- g_init_task = init_task;
-
- printk(KERN_INFO "boot: init thread created (pid=%d sid=%d pgid=%d)\n",
- init_task->pid,
- init_task->signal ? init_task->signal->sid : -1,
- init_task->signal ? init_task->signal->pgid : -1);
- } else {
- printk(KERN_ERR "boot: FAILED to create init thread!\n");
- }
-
-
- BOOT_STAGE(5, "core kernel init");
- printk(KERN_INFO "boot: BSP init complete, releasing APs\n");
-
- // Release APs with RELEASE semantics:
- // guarantees all global data is visible to APs.
- ap_boot_sync.store(1, __ATOMIC_RELEASE);
-
- // Start AP harts via SBI HSM extension.
- // OpenSBI v1.0+ parks non-boot harts in STOPPED state.
- // They must be explicitly started with sbi_hart_start().
- //
- // kernel_map.phys_addr was filled by setup_vm in arch/riscv/mm/init.cc
- // before MMU enable — it's the runtime PA at which the kernel was
- // loaded. APs start at the same PA as the BSP.
- {
- uint64 start_pa = kernel_map.phys_addr;
- if (start_pa == 0) {
- /* Defensive fallback if setup_vm hasn't filled the field. */
- start_pa =
- boot_va_to_pa(virt_addr(reinterpret_cast<uint64>(_start))).raw;
- }
- uint32 num_harts = 4; // TODO: read from FDT /cpus
- for (uint32 h = 0; h < num_harts; h++) {
- if (h == static_cast<uint32>(cpu_id))
- continue; // skip BSP
- auto ret = arch::sbi::sbi_hart_start(h, start_pa, fdt_paddr);
- if (ret.error == arch::sbi::SBI_SUCCESS) {
- printk(KERN_INFO "smp: started hart %u at PA 0x%llx\n", h, start_pa);
- } else {
- printk(KERN_ERR "smp: failed to start hart %u (err=%ld)\n", h,
- ret.error);
- }
- }
- }
-
- // BSP enables interrupts — first timer tick will preempt
- // into the scheduler loop, which will pick up PID 1.
- arch::cpu::intr_on();
-
- printk(KERN_INFO "boot: BSP entering idle loop\n");
-
- } else {
- // ========================================================
- // AP: spin until BSP finishes global init
- // ========================================================
- // DEBUG (commented out — uncomment for SMP debugging):
- // arch::sbi::sbi_ecall(1, 0, '1'); // AP entered kernel_main
-
- while (ap_boot_sync.load(__ATOMIC_ACQUIRE) == 0)
- ;
-
- // arch::sbi::sbi_ecall(1, 0, '2'); // AP passed ap_boot_sync
-
- // --- Per-CPU init (must happen before any printk!) ---
- // early_pgdir doesn't map UART MMIO, so we MUST switch to
- // BSP's kernel page table before any console output.
- vmm_init_ap(); // Switch to BSP-built kernel page table
-
- // arch::sbi::sbi_ecall(1, 0, '3'); // vmm_init_ap done
-
- trap_init_ap(); // Install stvec on this CPU
-
- // arch::sbi::sbi_ecall(1, 0, '4'); // trap_init_ap done
-
- printk(KERN_INFO "boot: AP online, cpu_id=%llu\n", cpu_id);
-
- sched_init_ap(); // Init per-CPU RunQueue, create Idle Task
-
- // arch::sbi::sbi_ecall(1, 0, '5'); // sched_init_ap done
-
- timer_init_ap(); // Start this CPU's timer interrupt
- arch::cpu::software_intr_on(); // Enable S-mode software interrupts for IPI
- arch::cpu::external_intr_on(); // Enable S-mode external interrupts for PLIC (UART)
-
- // AP is now a fully functional scheduler CPU.
- // Enable interrupts — first timer tick preempts into idle loop.
- arch::cpu::intr_on();
-
- printk(KERN_INFO "boot: AP cpu_id=%llu fully initialized\n", cpu_id);
- }
-
- // All CPUs reach here.
- // The "current task" is the boot stub (bsp_task or ap_task, pid=-1).
- // We must hand off to the scheduler's idle task by calling schedule().
- // Without this, the BSP runs as bsp_task (O1 class, timeslice=20 ticks)
- // instead of the idle task (which yields immediately when any task is
- // runnable). That delays PID 1 by up to 200ms and can stall entirely
- // if no timer tick fires before the first WFI.
- {
- TaskControlBlock *self = current_task();
- if (self) {
- self->state = TaskState::Dead; // Boot stub is done; don't re-enqueue
- }
- schedule(); // Switch to idle task (or init if it's already runnable)
- }
-
- // Should not reach here — schedule() switches away permanently.
- // But just in case, spin with WFI as a safety net.
- while (true) {
- arch::cpu::halt_until_interrupt();
- }
-}
diff --git a/na/kernel/module.mk b/na/kernel/module.mk
deleted file mode 100644
index 754a14c..0000000
--- a/na/kernel/module.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# kernel/module.mk
-SRCS_CC += kernel/main.cc
-SRCS_CC += kernel/sync/spinlock.cc
-SRCS_CC += kernel/sync/mutex.cc
-SRCS_CC += kernel/trap.cc
-SRCS_CC += kernel/irq.cc
-SRCS_CC += kernel/sched_core.cc
-SRCS_CC += kernel/sched_class_o1.cc
-SRCS_CC += kernel/timer.cc
-SRCS_CC += kernel/pid.cc
-SRCS_CC += kernel/pgrp.cc
-SRCS_CC += kernel/task.cc
-SRCS_CC += kernel/syscall.cc
-SRCS_CC += kernel/sys_process.cc
-SRCS_CC += kernel/sys_mm.cc
-SRCS_CC += kernel/sys_info.cc
-SRCS_CC += kernel/sys_fs.cc
-SRCS_CC += kernel/uaccess.cc
-SRCS_CC += kernel/waitqueue.cc
-SRCS_CC += kernel/signal.cc
-SRCS_CC += kernel/sys_signal.cc
-SRCS_CC += kernel/sys_misc.cc
-SRCS_CC += kernel/exec.cc
-SRCS_CC += kernel/poll.cc
-SRCS_CC += kernel/ptrace.cc
-SRCS_CC += kernel/smp.cc
diff --git a/na/kernel/pgrp.cc b/na/kernel/pgrp.cc
deleted file mode 100644
index e5b382c..0000000
--- a/na/kernel/pgrp.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Process Group / Session Implementation
- *
- * Hash table of signal_struct objects keyed by pgid. Lifecycle of
- * signal_struct mirrors Linux: one per thread group, refcounted across
- * CLONE_THREAD siblings, freed when the last task in the group exits.
- *
- * Reference: Linux kernel/pid.c, kernel/exit.c
- */
-
-#include <lume/pgrp.h>
-#include <lume/signal_types.h>
-#include <lume/task.h>
-#include <lume/new.h>
-#include <lume/klog.h>
-
-/* ================================================================== */
-/* Hash table state */
-/* ================================================================== */
-
-list_node g_pgrp_hash[kPgrpHashSize];
-Spinlock g_pgrp_lock;
-
-void pgrp_init()
-{
- for (auto& bucket : g_pgrp_hash) bucket.init();
- g_pgrp_lock.init("pgrp");
-}
-
-/* ================================================================== */
-/* Internal helpers (must be called with g_pgrp_lock held) */
-/* ================================================================== */
-
-static void pgrp_attach_locked(signal_struct* sig, int32 pgid)
-{
- sig->pgid = pgid;
- list_node* bucket = &g_pgrp_hash[pgrp_hash_idx(pgid)];
- sig->pgrp_link.prev = bucket->prev;
- sig->pgrp_link.next = bucket;
- bucket->prev->next = &sig->pgrp_link;
- bucket->prev = &sig->pgrp_link;
-}
-
-static void pgrp_detach_locked(signal_struct* sig)
-{
- /* Unlink only if currently on a list — init() makes prev/next point
- * to self, which we treat as "not linked". */
- if (sig->pgrp_link.next == &sig->pgrp_link) return;
- sig->pgrp_link.prev->next = sig->pgrp_link.next;
- sig->pgrp_link.next->prev = sig->pgrp_link.prev;
- sig->pgrp_link.init();
-}
-
-/* ================================================================== */
-/* signal_struct allocation / refcount */
-/* ================================================================== */
-
-signal_struct* alloc_signal_struct(int32 leader_pid, TaskControlBlock* leader)
-{
- auto* sig = new signal_struct;
- if (!sig) return nullptr;
- sig->init(leader_pid, leader);
-
- LockGuard guard(g_pgrp_lock);
- pgrp_attach_locked(sig, leader_pid);
- return sig;
-}
-
-signal_struct* fork_signal_struct(signal_struct* parent,
- TaskControlBlock* child_leader)
-{
- if (!parent) return nullptr;
-
- auto* sig = new signal_struct;
- if (!sig) return nullptr;
-
- /* Copy identity from parent, then override leader and attach. */
- sig->lock.init("signal");
- sig->refcount = 1;
- sig->leader = child_leader;
- sig->tty_old_pgrp = 0;
- sig->pgrp_link.init();
-
- {
- LockGuard pg(parent->lock);
- sig->pgid = parent->pgid;
- sig->sid = parent->sid;
- sig->tty = parent->tty;
- }
-
- LockGuard guard(g_pgrp_lock);
- pgrp_attach_locked(sig, sig->pgid);
- return sig;
-}
-
-void get_signal_struct(signal_struct* sig)
-{
- if (!sig) return;
- LockGuard guard(sig->lock);
- sig->refcount++;
-}
-
-void put_signal_struct(signal_struct* sig)
-{
- if (!sig) return;
-
- uint32 remaining;
- {
- LockGuard guard(sig->lock);
- remaining = --sig->refcount;
- }
- if (remaining != 0) return;
-
- /* Last reference — detach from pgrp hash and free. */
- {
- LockGuard guard(g_pgrp_lock);
- pgrp_detach_locked(sig);
- }
- delete sig;
-}
-
-/* ================================================================== */
-/* Pgrp membership change */
-/* ================================================================== */
-
-void change_pgid(signal_struct* sig, int32 new_pgid)
-{
- if (!sig) return;
- LockGuard guard(g_pgrp_lock);
- if (sig->pgid == new_pgid) return;
- pgrp_detach_locked(sig);
- pgrp_attach_locked(sig, new_pgid);
-}
-
-int pgrp_member_count(int32 pgid)
-{
- LockGuard guard(g_pgrp_lock);
- int n = 0;
- list_node* bucket = &g_pgrp_hash[pgrp_hash_idx(pgid)];
- for (list_node* node = bucket->next; node != bucket; node = node->next) {
- auto* sig = list_entry<signal_struct, &signal_struct::pgrp_link>(node);
- if (sig->pgid == pgid) n++;
- }
- return n;
-}
diff --git a/na/kernel/pid.cc b/na/kernel/pid.cc
deleted file mode 100644
index 04b846b..0000000
--- a/na/kernel/pid.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * PID Allocator — Bitmap-based O(1) block search
- * Reference: docs/specs/task.md §3.2
- */
-
-#include <lume/pid.h>
-
-void PidAllocator::init()
-{
- for (int i = 0; i < kBitmapWords; i++)
- bitmap_[i] = 0;
- next_hint_ = 1;
- bitmap_[0] |= 0x1ULL; /* Reserve PID 0 (idle/swapper) only.
- * PID 1 (init) is allocated normally — the first
- * kthread_create() call gets it, matching Linux. */
-}
-
-int32 PidAllocator::alloc()
-{
- LockGuard guard(lock_);
-
- int32 word_start = next_hint_ / 64;
-
- for (int32 passes = 0; passes < kBitmapWords; passes++) {
- int32 w = (word_start + passes) % kBitmapWords;
- uint64 free_bits = ~bitmap_[w];
- if (free_bits == 0)
- continue;
-
- int bit = 0;
- uint64 v = free_bits;
- while ((v & 1ULL) == 0) { v >>= 1; bit++; }
- int32 pid = w * 64 + bit;
-
- if (pid >= kMaxPid)
- continue;
-
- bitmap_[w] |= (1ULL << bit);
- next_hint_ = pid + 1;
- if (next_hint_ >= kMaxPid)
- next_hint_ = 2;
-
- return pid;
- }
-
- return -1;
-}
-
-void PidAllocator::free(int32 pid)
-{
- if (pid < 0 || pid >= kMaxPid)
- return;
-
- LockGuard guard(lock_);
- int32 w = pid / 64;
- int32 bit = pid % 64;
- bitmap_[w] &= ~(1ULL << bit);
-}
-
-PidAllocator g_pid_alloc;
diff --git a/na/kernel/poll.cc b/na/kernel/poll.cc
deleted file mode 100644
index 3674491..0000000
--- a/na/kernel/poll.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * sys_ppoll — POSIX ppoll() implementation.
- *
- * Linux fs/select.c:do_sys_poll() pattern, simplified for our needs:
- * - Two-phase loop: "register-then-check". First pass calls every
- * f_op->poll() with a real poll_table so drivers can register their
- * wait queues; if any fd is already ready we return immediately.
- * Otherwise schedule() until a wq fires (or timeout/signal), then
- * re-check with poll_table=nullptr (probe-only).
- *
- * Limitations vs. Linux:
- * - Only a single wait queue per ppoll call is tracked (the first one
- * a driver hands to poll_wait). Multi-fd polls work, but only the
- * first wq's wake actually re-runs the loop; other fds becoming
- * ready won't be noticed until the timer fires or another wake.
- * BusyBox interactive shell polls one fd (stdin), so this matches.
- *
- * Reference: Linux fs/select.c
- */
-
-#include <lume/poll.h>
-#include <lume/file.h>
-#include <lume/fdtable.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-#include <lume/timer.h>
-#include <lume/waitqueue.h>
-#include <lume/signal.h>
-#include <lume/signal_types.h>
-#include <lume/uaccess.h>
-#include <lume/errno.h>
-#include <lume/klog.h>
-#include <lume/new.h>
-
-/* ================================================================== */
-/* poll_wait — called by file_operations->poll() implementations */
-/* ================================================================== */
-
-void poll_wait(file* /*fp*/, WaitQueue* wq, poll_table* pt)
-{
- /* Probe-only pass: pt is null, drivers skip registration. */
- if (!pt || !wq) return;
- /* Single-wq design: keep the first one we see. A more complete
- * implementation would chain entries onto a list. */
- if (!pt->wq) pt->wq = wq;
-}
-
-/* ================================================================== */
-/* Linux-compatible timespec */
-/* ================================================================== */
-
-struct linux_timespec_pp {
- int64 tv_sec;
- int64 tv_nsec;
-};
-
-/*
- * Convert a timespec into a deadline in jiffies (g_jiffies clock).
- * Returns 0 if the input is null (= infinite wait), or a deadline
- * jiffy value otherwise. Caller treats 0 deadline as "no timeout".
- */
-static uint64 timespec_to_deadline(const linux_timespec_pp* ts)
-{
- if (!ts) return 0;
- /* g_jiffies advances at 100 Hz (HZ=100) — 1 jiffy = 10 ms */
- uint64 ticks = static_cast<uint64>(ts->tv_sec) * 100ULL;
- ticks += ts->tv_nsec / 10'000'000; /* 10 ms granularity */
- /* Round up sub-jiffy nsec so a tiny timeout doesn't truncate to 0. */
- if (ts->tv_nsec % 10'000'000 != 0) ticks++;
- return g_jiffies + ticks;
-}
-
-/* ================================================================== */
-/* do_poll — one scan over all fds */
-/* ================================================================== */
-
-/*
- * For each fd: look up the file, call its poll() callback. If `pt` is
- * non-null, drivers register their wait queues onto it. Returns the
- * number of fds that have any non-zero revents.
- */
-static int do_poll_scan(pollfd* fds, uint32 nfds, poll_table* pt)
-{
- TaskControlBlock* c = current_task();
- files_struct* files = c ? c->files : nullptr;
- if (!files) return 0;
- int ready = 0;
-
- for (uint32 i = 0; i < nfds; i++) {
- if (fds[i].fd < 0) {
- fds[i].revents = 0;
- continue;
- }
- file* fp = fcheck(files, fds[i].fd);
- if (!fp) {
- fds[i].revents = POLLNVAL;
- ready++;
- continue;
- }
- uint32 mask = 0;
- if (fp->f_op && fp->f_op->poll) {
- mask = fp->f_op->poll(fp, pt);
- } else {
- /* No poll handler — treat as always readable/writable so
- * a missing implementation doesn't permanently block sh. */
- mask = POLLIN | POLLOUT;
- }
- /* Filter: revents always include error/hup; userland event bits
- * only set if requested in events. */
- uint32 always = POLLERR | POLLHUP | POLLNVAL;
- uint32 want = static_cast<uint32>(fds[i].events) | always;
- fds[i].revents = static_cast<int16>(mask & want);
- if (fds[i].revents) ready++;
- }
- return ready;
-}
-
-/* ================================================================== */
-/* sys_ppoll */
-/* ================================================================== */
-
-/*
- * a0 = struct pollfd __user *ufds
- * a1 = nfds_t nfds
- * a2 = const struct __kernel_timespec __user *tsp (nullable = infinite)
- * a3 = const sigset_t __user *sigmask (nullable = no override)
- * a4 = size_t sigsetsize
- */
-int64 sys_ppoll(uint64 a0, uint64 a1, uint64 a2, uint64 a3,
- uint64 a4, uint64 /*a5*/)
-{
- uint64 ufds = a0;
- uint32 nfds = static_cast<uint32>(a1);
- uint64 utsp = a2;
- uint64 usigmask = a3;
- uint64 sigsetsize = a4;
-
- if (nfds > 1024) return -EINVAL; /* Linux RLIMIT_NOFILE-ish cap */
- if (usigmask && sigsetsize != sizeof(sigset_t)) return -EINVAL;
-
- /* Copy timespec from user. Null pointer means "wait forever". */
- bool have_timeout = (utsp != 0);
- linux_timespec_pp ts{};
- if (have_timeout) {
- if (copy_from_user(&ts, utsp, sizeof(ts)) < 0) return -EFAULT;
- if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1'000'000'000)
- return -EINVAL;
- }
- uint64 deadline = have_timeout ? timespec_to_deadline(&ts) : 0;
-
- /* Apply temporary sigmask if requested. Linux semantics: the new
- * mask is in effect for the duration of the call; on return we
- * restore the old mask. If a signal interrupts us mid-wait, the
- * original mask is restored before returning -EINTR. */
- TaskControlBlock* curr = current_task();
- sigset_t saved_mask{};
- bool mask_swapped = false;
- if (usigmask && curr && curr->sighand) {
- sigset_t newmask{};
- if (copy_from_user(&newmask, usigmask, sizeof(newmask)) < 0)
- return -EFAULT;
- newmask.del(SIGKILL);
- newmask.del(SIGSTOP);
- LockGuard guard(curr->sighand->siglock);
- saved_mask = curr->blocked;
- curr->blocked = newmask;
- mask_swapped = true;
- }
-
- /* Copy pollfd array from user. Allocate a kernel buffer; for the
- * common nfds<=8 case this is one slab object. */
- pollfd* kfds = nullptr;
- uint64 fds_bytes = static_cast<uint64>(nfds) * sizeof(pollfd);
- int64 ret = 0;
-
- if (nfds > 0) {
- kfds = static_cast<pollfd*>(operator new[](fds_bytes));
- if (!kfds) { ret = -ENOMEM; goto out_restore_mask; }
- if (copy_from_user(kfds, ufds, fds_bytes) < 0) {
- ret = -EFAULT; goto out_free;
- }
- }
-
- /* The main poll loop. */
- {
- poll_table pt{};
- pt.wq = nullptr;
- int n_ready = do_poll_scan(kfds, nfds, &pt);
-
- while (n_ready == 0) {
- /* Timeout==0 means non-blocking probe. */
- if (have_timeout && deadline <= g_jiffies) break;
-
- /* No registered wait queue? Nothing to sleep on. This
- * happens when no fd has a poll handler that calls
- * poll_wait — fall back to a short reschedule loop so we
- * don't burn the CPU. */
- if (!pt.wq) {
- schedule();
- if (signal_pending(curr)) { ret = -EINTR; goto out_free; }
- pt.wq = nullptr;
- n_ready = do_poll_scan(kfds, nfds, &pt);
- continue;
- }
-
- /* Standard prepare-check-schedule pattern. */
- pt.wq->prepare_to_wait(curr, TaskState::Sleeping);
-
- /* Re-probe with pt=nullptr (no extra registration) so we
- * see any state change that arrived between the previous
- * scan and prepare_to_wait. */
- n_ready = do_poll_scan(kfds, nfds, nullptr);
- if (n_ready > 0) {
- pt.wq->finish_wait(curr);
- break;
- }
- if (signal_pending(curr)) {
- pt.wq->finish_wait(curr);
- ret = -EINTR; goto out_free;
- }
-
- schedule();
- pt.wq->finish_wait(curr);
-
- /* Reset the registration and rescan — drivers may register
- * fresh wait queues this pass. */
- pt.wq = nullptr;
- n_ready = do_poll_scan(kfds, nfds, &pt);
- }
- ret = n_ready;
- }
-
- /* Copy revents back to user (events bits stay as user wrote them). */
- if (nfds > 0) {
- if (copy_to_user(ufds, kfds, fds_bytes) < 0) ret = -EFAULT;
- }
-
-out_free:
- if (kfds) operator delete[](kfds);
-
-out_restore_mask:
- if (mask_swapped && curr && curr->sighand) {
- LockGuard guard(curr->sighand->siglock);
- curr->blocked = saved_mask;
- }
- return ret;
-}
diff --git a/na/kernel/ptrace.cc b/na/kernel/ptrace.cc
deleted file mode 100644
index 3280c07..0000000
--- a/na/kernel/ptrace.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * PtRegs ↔ TrapFrame conversion
- *
- * This is the ONLY file that knows both the internal TrapFrame layout
- * and the Linux-ABI PtRegs layout. When TrapFrame is restructured
- * (e.g., to embed pt_regs directly), only this file needs to change.
- */
-
-#include <lume/ptrace.h>
-#include <arch/trapframe.h>
-
-void trapframe_to_pt_regs(PtRegs* dst, const arch::TrapFrame* src,
- uint64 scause, uint64 stval)
-{
- dst->epc = src->epc;
- dst->ra = src->ra;
- dst->sp = src->sp;
- dst->gp = src->gp;
- dst->tp = src->tp;
- dst->t0 = src->t[0];
- dst->t1 = src->t[1];
- dst->t2 = src->t[2];
- dst->s0 = src->s[0];
- dst->s1 = src->s[1];
- dst->a0 = src->a[0];
- dst->a1 = src->a[1];
- dst->a2 = src->a[2];
- dst->a3 = src->a[3];
- dst->a4 = src->a[4];
- dst->a5 = src->a[5];
- dst->a6 = src->a[6];
- dst->a7 = src->a[7];
- dst->s2 = src->s[2];
- dst->s3 = src->s[3];
- dst->s4 = src->s[4];
- dst->s5 = src->s[5];
- dst->s6 = src->s[6];
- dst->s7 = src->s[7];
- dst->s8 = src->s[8];
- dst->s9 = src->s[9];
- dst->s10 = src->s[10];
- dst->s11 = src->s[11];
- dst->t3 = src->t[3];
- dst->t4 = src->t[4];
- dst->t5 = src->t[5];
- dst->t6 = src->t[6];
- dst->status = src->sstatus;
- dst->badaddr = stval;
- dst->cause = scause;
- dst->orig_a0 = src->a[0]; /* Caller should set this before syscall dispatch */
-}
-
-void pt_regs_to_trapframe(arch::TrapFrame* dst, const PtRegs* src)
-{
- dst->epc = src->epc;
- dst->ra = src->ra;
- dst->sp = src->sp;
- dst->gp = src->gp;
- dst->tp = src->tp;
- dst->t[0] = src->t0;
- dst->t[1] = src->t1;
- dst->t[2] = src->t2;
- dst->s[0] = src->s0;
- dst->s[1] = src->s1;
- dst->a[0] = src->a0;
- dst->a[1] = src->a1;
- dst->a[2] = src->a2;
- dst->a[3] = src->a3;
- dst->a[4] = src->a4;
- dst->a[5] = src->a5;
- dst->a[6] = src->a6;
- dst->a[7] = src->a7;
- dst->s[2] = src->s2;
- dst->s[3] = src->s3;
- dst->s[4] = src->s4;
- dst->s[5] = src->s5;
- dst->s[6] = src->s6;
- dst->s[7] = src->s7;
- dst->s[8] = src->s8;
- dst->s[9] = src->s9;
- dst->s[10] = src->s10;
- dst->s[11] = src->s11;
- dst->t[3] = src->t3;
- dst->t[4] = src->t4;
- dst->t[5] = src->t5;
- dst->t[6] = src->t6;
- dst->sstatus = src->status;
- /* badaddr and cause are CSR values, not written back to TrapFrame */
-}
diff --git a/na/kernel/sched_class_o1.cc b/na/kernel/sched_class_o1.cc
deleted file mode 100644
index 28c693c..0000000
--- a/na/kernel/sched_class_o1.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * O(1) Priority Bitmap Scheduling Class
- */
-
-#include <lume/sched.h>
-#include <lume/sched_class.h>
-#include <lume/task.h>
-#include <lume/bitops.h>
-#include <kernel/sched_internal.h>
-
-static void o1_init_rq(RunQueue* rq)
-{
- rq->o1().init();
-}
-
-static void o1_enqueue_task(RunQueue* rq, TaskControlBlock* task)
-{
- rq->o1().enqueue(task);
- rq->inc_running();
-}
-
-static void o1_dequeue_task(RunQueue* rq, TaskControlBlock* task)
-{
- rq->o1().dequeue(task);
- rq->dec_running();
-}
-
-static TaskControlBlock* o1_pick_next_task(RunQueue* rq)
-{
- return rq->o1().pick_next();
-}
-
-static void o1_put_prev_task(RunQueue* /*rq*/, TaskControlBlock* /*task*/) {}
-
-int32 priority_to_timeslice(uint8 priority)
-{
- int32 ts = 20 - static_cast<int32>(priority) * 19 / 31;
- if (ts < 1) ts = 1;
- return ts;
-}
-
-static void o1_task_tick(RunQueue* rq, TaskControlBlock* curr)
-{
- if (curr == rq->idle()) {
- /* Idle task: if anything is runnable, yield immediately */
- if (rq->nr_running() > 0) {
- rq->set_need_resched(true);
- }
- return;
- }
-
- curr->time_slice--;
- if (curr->time_slice <= 0) {
- curr->time_slice = priority_to_timeslice(curr->priority);
- rq->set_need_resched(true);
- }
-}
-
-static void o1_yield_task(RunQueue* rq, TaskControlBlock* curr)
-{
- if (curr == rq->idle())
- return;
-
- curr->time_slice = 0;
- rq->set_need_resched(true);
-}
-
-const SchedClass g_sched_class_o1 = {
- .name = "o1",
- .next = &g_sched_class_idle,
- .init_rq = o1_init_rq,
- .enqueue_task = o1_enqueue_task,
- .dequeue_task = o1_dequeue_task,
- .pick_next_task = o1_pick_next_task,
- .put_prev_task = o1_put_prev_task,
- .task_tick = o1_task_tick,
- .yield_task = o1_yield_task,
-};
diff --git a/na/kernel/sched_core.cc b/na/kernel/sched_core.cc
deleted file mode 100644
index c0e068e..0000000
--- a/na/kernel/sched_core.cc
+++ /dev/null
@@ -1,571 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Scheduler Core
- *
- * The core walks sched_class chain to pick the next task. Each class
- * owns its own runqueue data and policy. This file owns the per-CPU
- * runqueue array and the scheduling state machine.
- */
-
-#include <lume/sched.h>
-#include <lume/sched_class.h>
-#include <lume/task.h>
-#include <lume/pmm.h>
-#include <lume/vmm.h>
-#include <lume/addr.h>
-#include <lume/new.h>
-#include <lume/kprintf.h>
-#include <lume/klog.h>
-#include <lume/smp.h>
-#include <arch/cpu.h>
-#include <arch/mmu.h>
-#include <arch/trap.h>
-#include <arch/context.h>
-#include <kernel/sched_internal.h>
-
-/* Assembly trampoline symbol (MD layer) */
-extern "C" void kthread_entry_trampoline();
-
-/* Per-CPU runqueue array */
-static RunQueue g_rq[kMaxCpus];
-
-/* sched_class chain head (highest priority first) */
-static const SchedClass* g_sched_class_head = &g_sched_class_o1;
-
-/* ========================================================================
- * Idle thread
- * ======================================================================== */
-
-static void idle_thread_func(void* /*arg*/)
-{
- while (true) {
- if (g_rq[arch::cpu::id()].need_resched()) {
- schedule();
- }
- arch::cpu::intr_on();
- arch::cpu::halt_until_interrupt();
- }
-}
-
-
-/* ========================================================================
- * Idle class (always returns idle task)
- * ======================================================================== */
-
-static void idle_init_rq(RunQueue* /*rq*/) {}
-static void idle_enqueue(RunQueue* /*rq*/, TaskControlBlock* /*task*/) {}
-static void idle_dequeue(RunQueue* /*rq*/, TaskControlBlock* /*task*/) {}
-static TaskControlBlock* idle_pick_next(RunQueue* rq) { return rq->idle(); }
-static void idle_put_prev(RunQueue* /*rq*/, TaskControlBlock* /*task*/) {}
-static void idle_task_tick(RunQueue* /*rq*/, TaskControlBlock* /*curr*/) {}
-static void idle_yield(RunQueue* /*rq*/, TaskControlBlock* /*curr*/) {}
-
-const SchedClass g_sched_class_idle = {
- .name = "idle",
- .next = nullptr,
- .init_rq = idle_init_rq,
- .enqueue_task = idle_enqueue,
- .dequeue_task = idle_dequeue,
- .pick_next_task = idle_pick_next,
- .put_prev_task = idle_put_prev,
- .task_tick = idle_task_tick,
- .yield_task = idle_yield,
-};
-
-/* ========================================================================
- * Internal helpers
- * ======================================================================== */
-
-static RunQueue* rq_for_cpu(uint32 cpu_id)
-{
- return &g_rq[cpu_id];
-}
-
-static RunQueue* this_rq()
-{
- return rq_for_cpu(arch::cpu::id());
-}
-
-static TaskControlBlock* pick_next_task(RunQueue* rq)
-{
- for (const SchedClass* c = g_sched_class_head; c; c = c->next) {
- TaskControlBlock* t = c->pick_next_task(rq);
- if (t) {
- return t;
- }
- }
- return rq->idle();
-}
-
-static const char* idle_name_for_cpu(uint32 cpu)
-{
- static char names[kMaxCpus][16];
- if (cpu >= kMaxCpus) {
- kernel_panic("sched: cpu_id out of range");
- }
-
- if (names[cpu][0] == '\0') {
- ksnprintf(names[cpu], sizeof(names[cpu]), "idle/%u", cpu);
- }
- return names[cpu];
-}
-
-/* ========================================================================
- * Kernel thread entry — MI-layer function called by MD trampoline
- * ======================================================================== */
-
-/* Forward declaration — C++ linkage (task.cc) */
-void task_exit(int code);
-
-extern "C" void kthread_entry(void (*entry)(void*), void* arg)
-{
- RunQueue* rq = this_rq();
- rq->lock().release();
-
- arch::cpu::intr_on();
-
- entry(arg);
-
- /* Entry function returned — clean exit (release PID, stack, TCB) */
- task_exit(0);
- __builtin_unreachable();
-}
-
-/* ========================================================================
- * fork_child_entry — called from ret_from_fork assembly
- * ======================================================================== */
-extern "C" void userret(arch::TrapFrame* tf);
-extern "C" void trap_vector();
-
-extern "C" void fork_child_entry(arch::TrapFrame* tf)
-{
- RunQueue* rq = this_rq();
- rq->lock().release();
-
- /* Set stvec to trap_vector for user-mode traps, then return to user */
- arch::trap::set_vector(reinterpret_cast<uint64>(trap_vector));
- userret(tf);
-
- __builtin_unreachable();
-}
-
-/* ========================================================================
- * alloc_task — canonical TCB allocator (defined in task.cc)
- *
- * Single point of initialization for TaskControlBlock. Ensures all
- * fields are properly zeroed when the struct gains new members.
- * ======================================================================== */
-
-extern TaskControlBlock* alloc_task(const char* name, uint8 priority,
- uint8 flags, uint8 cpu);
-
-/* ========================================================================
- * Public API: current_task()
- * ======================================================================== */
-
-TaskControlBlock* current_task()
-{
- return this_rq()->current();
-}
-
-/* ========================================================================
- * Public API: sched_enqueue / sched_dequeue
- * ======================================================================== */
-
-void sched_enqueue(TaskControlBlock* task)
-{
- RunQueue* rq = rq_for_cpu(task->cpu_id);
- LockGuard guard(rq->lock());
-
- if (!task->sched_class) {
- task->sched_class = &g_sched_class_o1;
- }
- if (!task->sched_entity) {
- task->sched_entity = &task->run_link;
- }
-
- task->sched_class->enqueue_task(rq, task);
-
- if (rq->current() == rq->idle() || task->priority < rq->current()->priority) {
- rq->set_need_resched(true);
- }
-}
-
-void sched_dequeue(TaskControlBlock* task)
-{
- RunQueue* rq = rq_for_cpu(task->cpu_id);
- LockGuard guard(rq->lock());
-
- if (!task->sched_class) {
- task->sched_class = &g_sched_class_o1;
- }
- task->sched_class->dequeue_task(rq, task);
-}
-
-/* ========================================================================
- * schedule() — main scheduling entry point
- * ======================================================================== */
-
-void schedule()
-{
- RunQueue* rq = this_rq();
- rq->lock().acquire();
-
- TaskControlBlock* prev = rq->current();
-
- if (prev && prev->state == TaskState::Running) {
- prev->state = TaskState::Runnable;
- prev->sched_class->enqueue_task(rq, prev);
- } else if (prev) {
- prev->sched_class->put_prev_task(rq, prev);
- }
-
- TaskControlBlock* next = pick_next_task(rq);
-
- if (prev == next) {
- /* We picked the same task. It must be Running again. */
- next->state = TaskState::Running;
- next->sched_class->dequeue_task(rq, next);
- rq->clear_need_resched();
- rq->lock().release();
- return;
- }
-
- if (next != rq->idle()) {
- next->sched_class->dequeue_task(rq, next);
- }
-
- next->state = TaskState::Running;
- rq->set_current(next);
- rq->clear_need_resched();
-
- /* Switch address space (satp) if the new task has a different mm.
- * Kernel threads (mm == nullptr) borrow the previous task's mm,
- * so no satp switch is needed — this matches Linux's lazy TLB behavior. */
- {
- VmSpace* prev_mm = prev ? prev->mm : nullptr;
- VmSpace* next_mm = next->mm;
- uint32 cpu = static_cast<uint32>(arch::cpu::id());
-
- if (next_mm && next_mm != prev_mm) {
- /* Switching TO a user process with a different address space.
- * Linux: switch_mm_irqs_off() in arch/riscv/mm/context.c
- * sets satp and flushes TLB. */
- arch::mmu::set_page_table(next_mm->root_pa);
- arch::mmu::flush_tlb_all();
- next_mm->cpu_mask.fetch_or(1ULL << cpu, __ATOMIC_RELAXED);
-
- /* Critical: update TrapFrame's kernel_satp so that trap_vector.S
- * restores the correct page table on next trap entry.
- * Without this, a process migrated to a new CPU would have its
- * trap entry use a stale satp from when trapframe_init_user()
- * was called, pointing to whatever page table was active then
- * on the ORIGINAL CPU. */
- if (next->trapframe) {
- next->trapframe->kernel_satp = arch::mmu::read_satp();
- }
- }
-
- if (prev_mm && prev_mm != next_mm) {
- /* Leaving prev's address space — clear our bit */
- prev_mm->cpu_mask.fetch_and(~(1ULL << cpu), __ATOMIC_RELAXED);
- }
- }
-
- /* FPU lazy save/restore.
- * Save prev's FP registers only if they were modified (FS=Dirty).
- * Restore next's FP state for user tasks; disable FPU for kthreads. */
- if (arch::fpu_state_dirty(arch::fpu_get_fs())) {
- fpu_save(&prev->fpstate);
- }
- if (next->mm != nullptr) {
- arch::fpu_set_fs(arch::kSstatusFS_Clean);
- fpu_restore(&next->fpstate);
- } else {
- arch::fpu_set_fs(arch::kSstatusFS_Off);
- }
-
- arch::context_switch(&prev->context, &next->context);
-
- rq->lock().release();
-}
-
-/* ========================================================================
- * sched_tick() — called from timer interrupt (MI trap layer)
- * ======================================================================== */
-
-void sched_tick()
-{
- RunQueue* rq = this_rq();
- {
- LockGuard guard(rq->lock());
- TaskControlBlock* curr = rq->current();
- if (!curr) return;
- curr->sched_class->task_tick(rq, curr);
- }
-
- /* Periodic load balancing — runs without holding rq lock */
- sched_balance();
-}
-
-/* ========================================================================
- * sched_check_preempt() — called from trap return path
- *
- * Only preempts if:
- * 1. need_resched flag is set (a higher-priority task is ready)
- * 2. preempt_count == 0 (no spinlocks held, safe to context switch)
- * ======================================================================== */
-
-void sched_check_preempt()
-{
- RunQueue* rq = this_rq();
- if (!rq->need_resched()) return;
-
- TaskControlBlock* curr = current_task();
- if (curr && curr->preempt_count > 0) {
- /* Cannot preempt — caller is in a critical section.
- * The preemption will fire when the last lock is released. */
- return;
- }
- schedule();
-}
-
-/* ========================================================================
- * sched_yield() — voluntary yield
- * ======================================================================== */
-
-void sched_yield()
-{
- RunQueue* rq = this_rq();
- {
- LockGuard guard(rq->lock());
- TaskControlBlock* curr = rq->current();
- if (curr && curr != rq->idle()) {
- curr->sched_class->yield_task(rq, curr);
- rq->set_need_resched(true);
- }
- }
- schedule();
-}
-
-/* ========================================================================
- * sched_init() — BSP initialization
- * ======================================================================== */
-
-void sched_init()
-{
- for (int i = 0; i < kMaxCpus; i++) {
- RunQueue* rq = &g_rq[i];
- rq->init(static_cast<uint32>(i));
-
- g_sched_class_o1.init_rq(rq);
- g_sched_class_idle.init_rq(rq);
- }
-
- g_sched_class_head = &g_sched_class_o1;
-
- uint64 bsp_cpu = arch::cpu::id();
- uint8 bsp_cpu8 = static_cast<uint8>(bsp_cpu);
-
- TaskControlBlock* idle0 = alloc_task(idle_name_for_cpu(static_cast<uint32>(bsp_cpu)),
- kIdlePriority,
- TF_KTHREAD | TF_IDLE, bsp_cpu8);
- if (!idle0) kernel_panic("sched_init: failed to create idle/bsp");
-
- idle0->pid = 0;
- idle0->tgid = 0;
- idle0->sched_class = &g_sched_class_idle;
- idle0->sched_entity = nullptr;
-
- uint64 stack_top = idle0->kstack_va + kKernelStackSize
- - sizeof(arch::TrapFrame);
- idle0->context = {};
- idle0->context.ra = reinterpret_cast<uint64>(kthread_entry_trampoline);
- idle0->context.sp = stack_top;
- idle0->context.s[0] = reinterpret_cast<uint64>(idle_thread_func);
- idle0->context.s[1] = 0;
-
- g_rq[bsp_cpu].set_idle(idle0);
-
- TaskControlBlock* bsp_task = alloc_task("kernel_main", 0, TF_KTHREAD, bsp_cpu8);
- if (!bsp_task) kernel_panic("sched_init: failed to create bsp_task");
-
- bsp_task->pid = -1;
- bsp_task->tgid = -1;
- bsp_task->state = TaskState::Running;
-
- g_rq[bsp_cpu].set_current(bsp_task);
-}
-
-/* ========================================================================
- * sched_init_ap() — AP per-CPU initialization
- * ======================================================================== */
-
-void sched_init_ap()
-{
- uint64 cpu = arch::cpu::id();
-
- const char* name = idle_name_for_cpu(static_cast<uint32>(cpu));
-
- TaskControlBlock* idle = alloc_task(name, kIdlePriority,
- TF_KTHREAD | TF_IDLE,
- static_cast<uint8>(cpu));
- if (!idle) kernel_panic("sched_init_ap: failed to create idle");
-
- idle->pid = 0;
- idle->tgid = 0;
- idle->sched_class = &g_sched_class_idle;
- idle->sched_entity = nullptr;
-
- uint64 stack_top = idle->kstack_va + kKernelStackSize
- - sizeof(arch::TrapFrame);
- idle->context = {};
- idle->context.ra = reinterpret_cast<uint64>(kthread_entry_trampoline);
- idle->context.sp = stack_top;
- idle->context.s[0] = reinterpret_cast<uint64>(idle_thread_func);
- idle->context.s[1] = 0;
-
- g_rq[cpu].set_idle(idle);
-
- TaskControlBlock* ap_task = alloc_task("ap_boot", 0, TF_KTHREAD,
- static_cast<uint8>(cpu));
- if (!ap_task) kernel_panic("sched_init_ap: failed to create ap_task");
-
- ap_task->pid = -1;
- ap_task->tgid = -1;
- ap_task->state = TaskState::Running;
-
- g_rq[cpu].set_current(ap_task);
-}
-
-/* ========================================================================
- * sched_nr_running() — runnable task count for a CPU
- * ======================================================================== */
-
-uint32 sched_nr_running(uint32 cpu_id)
-{
- if (cpu_id >= kMaxCpus) return 0;
- return g_rq[cpu_id].o1().nr_running();
-}
-
-/* ========================================================================
- * sched_migrate_task() — move a task between CPUs
- *
- * Locks are acquired in cpu_id order to prevent ABBA deadlock.
- * The task must be Runnable (in a runqueue) and not currently running.
- * ======================================================================== */
-
-int sched_migrate_task(TaskControlBlock* task, uint8 target_cpu)
-{
- if (!task || target_cpu >= kMaxCpus) return -22; /* -EINVAL */
-
- uint8 src_cpu = task->cpu_id;
- if (src_cpu == target_cpu) return 0; /* already there */
-
- /* Cannot migrate idle or currently-running tasks */
- if (task->flags & TF_IDLE) return -22;
-
- RunQueue* src_rq = rq_for_cpu(src_cpu);
- RunQueue* dst_rq = rq_for_cpu(target_cpu);
-
- /* Ordered lock acquisition: always lock lower cpu_id first */
- if (src_cpu < target_cpu) {
- src_rq->lock().acquire();
- dst_rq->lock().acquire();
- } else {
- dst_rq->lock().acquire();
- src_rq->lock().acquire();
- }
-
- int ret = -22;
-
- /* Double-check: task must not be current on either CPU */
- if (task != src_rq->current() && task->state == TaskState::Runnable) {
- /* Dequeue from source */
- task->sched_class->dequeue_task(src_rq, task);
-
- /* Update cpu_id and enqueue on destination */
- task->cpu_id = target_cpu;
- task->sched_class->enqueue_task(dst_rq, task);
-
- /* Signal target CPU to reschedule */
- dst_rq->set_need_resched(true);
- ret = 0;
- }
-
- /* Release in reverse order */
- if (src_cpu < target_cpu) {
- dst_rq->lock().release();
- src_rq->lock().release();
- } else {
- src_rq->lock().release();
- dst_rq->lock().release();
- }
-
- /* If migration succeeded, send reschedule IPI to target */
- if (ret == 0) {
- smp_send_reschedule(target_cpu);
- }
-
- return ret;
-}
-
-/* ========================================================================
- * sched_balance() — periodic load balancer (pull model)
- *
- * Called from sched_tick() context. Looks at all other CPUs and if any
- * has 2+ more runnable tasks than the local CPU, pulls one task.
- * This is a simplified Linux-style load_balance().
- * ======================================================================== */
-
-void sched_balance()
-{
- uint32 this_cpu = static_cast<uint32>(arch::cpu::id());
- uint32 local_load = g_rq[this_cpu].o1().nr_running();
-
- /* Only balance if we're idle or lightly loaded */
- if (local_load > 1) return;
-
- uint32 busiest_cpu = this_cpu;
- uint32 busiest_load = local_load;
-
- /* Find the busiest CPU */
- for (uint32 cpu = 0; cpu < kMaxCpus; cpu++) {
- if (cpu == this_cpu) continue;
- uint32 load = g_rq[cpu].o1().nr_running();
- if (load > busiest_load) {
- busiest_load = load;
- busiest_cpu = cpu;
- }
- }
-
- /* Only pull if imbalance is >= 2 */
- if (busiest_cpu == this_cpu || busiest_load < local_load + 2) return;
-
- /* Try to pull one task from the busiest CPU */
- RunQueue* busy_rq = rq_for_cpu(busiest_cpu);
-
- /* Lock ordering: lower cpu_id first */
- RunQueue* first = (this_cpu < busiest_cpu)
- ? &g_rq[this_cpu] : busy_rq;
- RunQueue* second = (this_cpu < busiest_cpu)
- ? busy_rq : &g_rq[this_cpu];
-
- first->lock().acquire();
- second->lock().acquire();
-
- /* Re-check after acquiring locks */
- TaskControlBlock* victim = busy_rq->o1().pick_next();
- if (victim && victim != busy_rq->current()
- && !(victim->flags & TF_IDLE)) {
- victim->sched_class->dequeue_task(busy_rq, victim);
- victim->cpu_id = static_cast<uint8>(this_cpu);
- victim->sched_class->enqueue_task(&g_rq[this_cpu], victim);
- g_rq[this_cpu].set_need_resched(true);
- }
-
- second->lock().release();
- first->lock().release();
-}
diff --git a/na/kernel/sched_internal.h b/na/kernel/sched_internal.h
deleted file mode 100644
index 514b613..0000000
--- a/na/kernel/sched_internal.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-#include <lume/types.h>
-#include <lume/list.h>
-#include <lume/sched.h>
-#include <lume/config.h>
-#include <lume/bitops.h>
-#include <lume/task.h>
-#include <kernel/sync/spinlock.h>
-
-class O1RunQueue {
-public:
- void init() {
- bitmap_ = 0;
- nr_running_ = 0;
- for (int p = 0; p < kNumPriorities; p++) {
- queues_[p].init();
- }
- }
-
- void enqueue(TaskControlBlock* task) {
- int prio = task->priority;
-
- list_node* head = &queues_[prio];
- task->run_link.prev = head->prev;
- task->run_link.next = head;
- head->prev->next = &task->run_link;
- head->prev = &task->run_link;
-
- bitmap_ |= (1U << prio);
- nr_running_++;
-
- task->state = TaskState::Runnable;
- }
-
- void dequeue(TaskControlBlock* task) {
- int prio = task->priority;
-
- task->run_link.prev->next = task->run_link.next;
- task->run_link.next->prev = task->run_link.prev;
- task->run_link.init();
-
- if (queues_[prio].is_empty()) {
- bitmap_ &= ~(1U << prio);
- }
-
- if (nr_running_ > 0) {
- nr_running_--;
- }
- }
-
- TaskControlBlock* pick_next() const {
- if (bitmap_ == 0) {
- return nullptr;
- }
-
- int prio = lume::ctz32(bitmap_);
- list_node* node = queues_[prio].next;
- return list_entry<TaskControlBlock, &TaskControlBlock::run_link>(node);
- }
-
- uint32 nr_running() const { return nr_running_; }
-
-private:
- uint32 bitmap_ = 0;
- list_node queues_[kNumPriorities];
- uint32 nr_running_ = 0;
-};
-
-class RunQueue {
-public:
- void init(uint32 cpu_id) {
- lock_.init("RunQueue");
- cpu_id_ = cpu_id;
- nr_running_ = 0;
- need_resched_ = false;
- current_ = nullptr;
- idle_ = nullptr;
- }
-
- Spinlock& lock() { return lock_; }
-
- uint32 cpu_id() const { return cpu_id_; }
-
- uint32 nr_running() const { return nr_running_; }
- void inc_running() { nr_running_++; }
- void dec_running() { if (nr_running_ > 0) nr_running_--; }
-
- bool need_resched() const { return need_resched_; }
- void set_need_resched(bool v) { need_resched_ = v; }
- void clear_need_resched() { need_resched_ = false; }
-
- TaskControlBlock* current() const { return current_; }
- void set_current(TaskControlBlock* task) { current_ = task; }
-
- TaskControlBlock* idle() const { return idle_; }
- void set_idle(TaskControlBlock* task) { idle_ = task; }
-
- O1RunQueue& o1() { return o1_; }
- const O1RunQueue& o1() const { return o1_; }
-
-private:
- Spinlock lock_;
- uint32 cpu_id_ = 0;
- uint32 nr_running_ = 0;
- bool need_resched_ = false;
-
- TaskControlBlock* current_ = nullptr;
- TaskControlBlock* idle_ = nullptr;
-
- O1RunQueue o1_;
-};
diff --git a/na/kernel/signal.cc b/na/kernel/signal.cc
deleted file mode 100644
index 53d26da..0000000
--- a/na/kernel/signal.cc
+++ /dev/null
@@ -1,442 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Signal Delivery — Queue, Dequeue, Default Actions, and do_signal
- *
- * Implements the core signal infrastructure:
- * - send_signal(): enqueue a signal to a task's pending set
- * - force_sig_fault(): fault-induced signal (trap handler API)
- * - do_signal(): called on return-to-user, delivers pending signals
- * - next_signal(): dequeue the highest-priority deliverable signal
- *
- * Signal frame construction (arch-specific) is in arch/riscv/signal.cc.
- *
- * Reference: Linux kernel/signal.c
- */
-
-#include <lume/signal.h>
-#include <lume/signal_types.h>
-#include <lume/signal_nr.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-#include <lume/wait.h>
-#include <lume/console.h>
-#include <lume/kprintf.h>
-#include <lume/klog.h>
-#include <lume/new.h>
-#include <lume/errno.h>
-#include <arch/trapframe.h>
-
-/* Forward declaration: arch-specific signal frame setup */
-int setup_signal_frame(arch::TrapFrame* tf, int signo,
- kernel_sigaction* ka, kernel_siginfo* info);
-
-/* ================================================================== */
-/* sighand_struct lifecycle (refcount-shared, Linux copy_sighand) */
-/* ================================================================== */
-
-sighand_struct* alloc_sighand()
-{
- auto* sh = new sighand_struct;
- if (sh) sh->init();
- return sh;
-}
-
-sighand_struct* copy_sighand(sighand_struct* parent)
-{
- /* Linux: kernel/fork.c:copy_sighand() when !CLONE_SIGHAND copies
- * the action table verbatim so the child inherits dispositions
- * without sharing future modifications. */
- if (!parent) return alloc_sighand();
- auto* sh = new sighand_struct;
- if (!sh) return nullptr;
- sh->init();
- LockGuard guard(parent->siglock);
- for (int i = 0; i < _NSIG; i++) sh->action[i] = parent->action[i];
- return sh;
-}
-
-void get_sighand(sighand_struct* sh)
-{
- if (!sh) return;
- LockGuard guard(sh->siglock);
- sh->refcount++;
-}
-
-void put_sighand(sighand_struct* sh)
-{
- if (!sh) return;
- uint32 remaining;
- {
- LockGuard guard(sh->siglock);
- remaining = --sh->refcount;
- }
- if (remaining == 0) delete sh;
-}
-
-/* ================================================================== */
-/* next_signal — find lowest-numbered deliverable signal */
-/* ================================================================== */
-
-/* Manual count-trailing-zeros (no libgcc __ctzdi2 dependency) */
-static inline int sig_ctz64(uint64 x)
-{
- if (x == 0) return 64;
- int n = 0;
- if ((x & 0xFFFFFFFF) == 0) { n += 32; x >>= 32; }
- if ((x & 0x0000FFFF) == 0) { n += 16; x >>= 16; }
- if ((x & 0x000000FF) == 0) { n += 8; x >>= 8; }
- if ((x & 0x0000000F) == 0) { n += 4; x >>= 4; }
- if ((x & 0x00000003) == 0) { n += 2; x >>= 2; }
- if ((x & 0x00000001) == 0) { n += 1; }
- return n;
-}
-
-static int next_signal(sigpending* pending, const sigset_t* mask)
-{
- /* Standard signals: check bitmap & ~blocked */
- for (int i = 0; i < _NSIG_WORDS; i++) {
- uint64 bits = pending->signal.sig[i] & ~mask->sig[i];
- if (bits != 0) {
- int sig = i * 64 + sig_ctz64(bits) + 1;
- return sig;
- }
- }
- return 0;
-}
-
-/* ================================================================== */
-/* dequeue_signal — remove a signal from the pending set */
-/* ================================================================== */
-
-/*
- * Dequeue the next deliverable signal. MUST be called with
- * task->sighand->siglock held. Mirrors Linux's __dequeue_signal()
- * (kernel/signal.c) which requires the same lock.
- */
-static int dequeue_signal_locked(TaskControlBlock* task, kernel_siginfo* info)
-{
- int sig = next_signal(&task->pending, &task->blocked);
- if (sig == 0) return 0;
-
- /* Clear the bitmap bit */
- task->pending.signal.del(sig);
-
- /* Search the queue for a matching sigqueue entry */
- list_node* node = task->pending.list.next;
- while (node != &task->pending.list) {
- auto* q = list_entry<sigqueue, &sigqueue::list>(node);
- if (q->info.si_signo == sig) {
- /* Remove from queue */
- node->prev->next = node->next;
- node->next->prev = node->prev;
- if (info) *info = q->info;
- delete q;
- return sig;
- }
- node = node->next;
- }
-
- /* No queued entry — fill in minimal info */
- if (info) {
- info->si_signo = sig;
- info->si_errno = 0;
- info->si_code = 0; /* SI_USER */
- }
- return sig;
-}
-
-/* ================================================================== */
-/* send_signal — enqueue a signal to a task */
-/* ================================================================== */
-
-/*
- * Drop a queued sigqueue entry with the given signo from `task`.
- *
- * MUST be called with task->sighand->siglock held — Linux's
- * flush_sigqueue_mask() has the same precondition (kernel/signal.c).
- */
-static bool discard_pending_locked(TaskControlBlock* task, int signo)
-{
- bool any = false;
- list_node* node = task->pending.list.next;
- while (node != &task->pending.list) {
- list_node* nxt = node->next;
- auto* q = list_entry<sigqueue, &sigqueue::list>(node);
- if (q->info.si_signo == signo) {
- node->prev->next = node->next;
- node->next->prev = node->prev;
- delete q;
- any = true;
- }
- node = nxt;
- }
- task->pending.signal.del(signo);
- return any;
-}
-
-static inline bool is_stop_sig(int sig)
-{
- return sig == SIGSTOP || sig == SIGTSTP ||
- sig == SIGTTIN || sig == SIGTTOU;
-}
-
-/*
- * send_signal — queue a signal to a task.
- *
- * Acquires task->sighand->siglock for the full update (bitmap, queue,
- * stop/cont side-effects, scheduler wake). Mirrors Linux's
- * __send_signal_locked() which also requires this lock (see
- * kernel/signal.c:1048 lockdep_assert_held).
- *
- * Returns 0 on success or a negative errno.
- */
-int send_signal(int sig, kernel_siginfo* info, TaskControlBlock* task)
-{
- if (sig < 1 || sig > _NSIG) return -EINVAL;
- if (!task) return -ESRCH;
- if (!task->sighand) return -ESRCH; /* eager-alloc invariant; tasks
- * without sighand can't receive
- * signals (idle / early kthreads) */
-
- LockGuard guard(task->sighand->siglock);
-
- /*
- * Linux's prepare_signal(): a stop signal cancels any pending
- * SIGCONT and vice versa, so the most recent decision wins.
- */
- if (is_stop_sig(sig)) {
- discard_pending_locked(task, SIGCONT);
- } else if (sig == SIGCONT) {
- discard_pending_locked(task, SIGSTOP);
- discard_pending_locked(task, SIGTSTP);
- discard_pending_locked(task, SIGTTIN);
- discard_pending_locked(task, SIGTTOU);
-
- /* SIGCONT's resume effect happens atomically with delivery. */
- if (task->state == TaskState::Stopped) {
- task->state = TaskState::Runnable;
- task->notify_continued = true;
- task->stopped_code = 0;
- sched_enqueue(task);
- if (task->parent) task->parent->wait_chld.wake_all();
- }
- }
-
- /* Already pending? For standard signals, just mark the bitmap */
- if (sig < SIGRTMIN && task->pending.signal.has(sig))
- return 0;
-
- /* Enqueue a sigqueue entry */
- auto* q = new sigqueue;
- if (!q) {
- /* Can't allocate — just set the bitmap (signal still delivered) */
- task->pending.signal.add(sig);
- return 0;
- }
-
- q->list.init();
- if (info) {
- q->info = *info;
- } else {
- q->info.si_signo = sig;
- q->info.si_errno = 0;
- q->info.si_code = 0; /* SI_USER */
- }
- q->info.si_signo = sig; /* ensure consistent */
-
- /* Add to tail of queue */
- list_node* head = &task->pending.list;
- q->list.prev = head->prev;
- q->list.next = head;
- head->prev->next = &q->list;
- head->prev = &q->list;
-
- /* Set bitmap */
- task->pending.signal.add(sig);
-
- /* Wake the task if it's sleeping in an interruptible wait. */
- if (task->state == TaskState::Sleeping) {
- task->state = TaskState::Runnable;
- sched_enqueue(task);
- }
-
- /* SIGKILL must run even if the target is Stopped. */
- if (sig == SIGKILL && task->state == TaskState::Stopped) {
- task->state = TaskState::Runnable;
- task->stopped_code = 0;
- sched_enqueue(task);
- }
-
- return 0;
-}
-
-/* ================================================================== */
-/* force_sig_fault — trap handler API */
-/* ================================================================== */
-
-void force_sig_fault(int signo, int code, uint64 addr)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) return;
-
- /* Kernel threads cannot handle signals — kill immediately */
- if (curr->flags & TF_KTHREAD) {
- pr_err("signal: kernel thread %s received signal %d\n",
- curr->name ? curr->name : "(noname)", signo);
- task_kill_current(signo);
- return;
- }
-
- /* Sighand must already exist for user-mode tasks (allocated at
- * fork / kernel_execve time). If it's somehow missing, the only
- * safe action is to terminate. */
- if (!curr->sighand) {
- pr_err("signal: pid %d has no sighand, killing\n", curr->pid);
- task_kill_current(signo);
- return;
- }
-
- kernel_siginfo info{};
- info.si_signo = signo;
- info.si_code = code;
- info._sifields._sigfault._addr = addr;
-
- /* Force: bypass blocked mask for fatal signals (lock to keep
- * blocked-mask updates consistent with sigprocmask). */
- {
- LockGuard guard(curr->sighand->siglock);
- curr->blocked.del(signo);
- }
-
- send_signal(signo, &info, curr);
-}
-
-/* ================================================================== */
-/* signal_pending — check if unblocked signals are pending */
-/* ================================================================== */
-
-bool signal_pending(TaskControlBlock* task)
-{
- /* Advisory check (matches Linux's recalc_sigpending() usage):
- * callers re-validate under siglock before acting. The bitmap is
- * a uint64 word; a torn read can only produce false-positives or
- * false-negatives transiently, never a corrupt pointer. */
- if (!task) return false;
- sigset_t unblocked = task->pending.signal & ~task->blocked;
- return !unblocked.is_empty();
-}
-
-/* ================================================================== */
-/* do_signal — called before returning to user mode */
-/* ================================================================== */
-
-void do_signal(arch::TrapFrame* tf)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) return;
-
- /* Kernel threads never deliver signals via frames */
- if (curr->flags & TF_KTHREAD) return;
-
- /* No sighand means no signal infrastructure for this task */
- if (!curr->sighand) return;
-
- while (true) {
- kernel_siginfo info{};
- kernel_sigaction ka_copy;
- int sig;
-
- /*
- * Take siglock for dequeue + action snapshot. Action processing
- * (default actions like SIG_ACT_TERM, or constructing a signal
- * frame for a user handler) is done OUTSIDE the lock — it can
- * touch user memory and even call schedule(). This matches
- * Linux's get_signal() loop in kernel/signal.c: it takes
- * siglock around dequeue_signal+ka_copy, drops it before
- * dispatching, and re-acquires next iteration.
- */
- {
- LockGuard guard(curr->sighand->siglock);
- sig = dequeue_signal_locked(curr, &info);
- if (sig == 0) break;
- ka_copy = curr->sighand->action[sig - 1];
- }
- kernel_sigaction* ka = &ka_copy;
-
- /* SIG_IGN: discard */
- if (ka->sa_handler == SIG_IGN && !sig_kernel_only(sig))
- continue;
-
- /* SIG_DFL: apply default action */
- if (ka->sa_handler == SIG_DFL) {
- SigDefaultAction act = sig_default_action(sig);
- switch (act) {
- case SIG_ACT_IGN:
- continue;
- case SIG_ACT_TERM:
- case SIG_ACT_CORE:
- pr_debug_s(KLOG_SUBSYS_SIGNAL,
- "signal: pid %d killed by signal %d\n",
- curr->pid, sig);
- task_kill_current(sig);
- return; /* never reached */
- case SIG_ACT_STOP: {
- /*
- * Enter Stopped state. Record the encoded wstatus so
- * the parent's wait4(WUNTRACED) can observe and report.
- * The schedule() below does NOT re-enqueue Stopped
- * tasks — when SIGCONT eventually arrives, send_signal
- * flips us back to Runnable and re-enqueues.
- */
- curr->stopped_code = W_STOPCODE(sig);
- curr->state = TaskState::Stopped;
- if (curr->parent)
- curr->parent->wait_chld.wake_all();
- pr_debug_s(KLOG_SUBSYS_SIGNAL,
- "signal: pid %d stopped by sig %d\n",
- curr->pid, sig);
- schedule();
- /*
- * Resumed (either by SIGCONT or SIGKILL). Loop back to
- * deliver any remaining pending signals before returning
- * to user mode.
- */
- continue;
- }
- case SIG_ACT_CONT:
- /* SIGCONT's resume side-effect already happened in
- * send_signal(). The only thing left at handler time
- * is to consume the signal (no-op for SIG_DFL). */
- continue;
- }
- }
-
- /* User handler: construct signal frame */
- pr_debug_s(KLOG_SUBSYS_SIGNAL,
- "signal: pid %d delivering sig %d handler=%llx\n",
- curr->pid, sig, reinterpret_cast<uint64>(ka->sa_handler));
- int ret = setup_signal_frame(tf, sig, ka, &info);
- if (ret < 0) {
- pr_err("signal: failed to set up frame for sig %d\n", sig);
- task_kill_current(sig);
- return;
- }
-
- /* Post-delivery bookkeeping under siglock — both the per-task
- * blocked mask and the shared action table (SA_RESETHAND) are
- * touched, so we must hold the same lock the senders use. */
- {
- LockGuard guard(curr->sighand->siglock);
- if (!(ka->sa_flags & SA_NODEFER)) curr->blocked.add(sig);
- curr->blocked = curr->blocked | ka->sa_mask;
- if (ka->sa_flags & SA_RESETHAND) {
- curr->sighand->action[sig - 1].sa_handler = SIG_DFL;
- }
- }
-
- /* Only deliver one signal per return-to-user */
- break;
- }
-}
diff --git a/na/kernel/smp.cc b/na/kernel/smp.cc
deleted file mode 100644
index 1a79ac3..0000000
--- a/na/kernel/smp.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * smp.cc — SMP IPI implementation
- *
- * Sending side: atomically set reason bits in target CPU's IpiData,
- * then invoke arch-specific IPI delivery (SBI ecall on RISC-V).
- *
- * Receiving side: smp_handle_ipi() is called from the trap handler
- * when an S-mode software interrupt arrives. It atomically consumes
- * all pending reason bits and dispatches to the appropriate handler.
- *
- * Reference: docs/specs/linux_abi_mm_plan.md §SMP §2.2
- */
-
-#include <lume/smp.h>
-#include <lume/klog.h>
-#include <lume/sched.h>
-#include <arch/cpu.h>
-#include <arch/mmu.h>
-#include <arch/sbi.h>
-
-/* Per-CPU IPI data — BSS zero-initialized */
-IpiData g_ipi_data[kMaxCpus];
-
-/* ------------------------------------------------------------------ */
-/* smp_init */
-/* ------------------------------------------------------------------ */
-
-void smp_init()
-{
- for (uint32 i = 0; i < kMaxCpus; i++) {
- g_ipi_data[i].pending.store(0, __ATOMIC_RELAXED);
- g_ipi_data[i].tlb_start = 0;
- g_ipi_data[i].tlb_end = 0;
- }
-
- printk(KERN_INFO "smp: IPI framework initialized (%u CPUs max)\n",
- static_cast<unsigned>(kMaxCpus));
-}
-
-/* ------------------------------------------------------------------ */
-/* smp_send_ipi — send IPI to target CPUs */
-/* ------------------------------------------------------------------ */
-
-void smp_send_ipi(uint64 cpu_mask, uint32 reason)
-{
- if (cpu_mask == 0) return;
-
- /* Set pending bits on each target CPU's IpiData */
- for (uint32 cpu = 0; cpu < kMaxCpus; cpu++) {
- if (cpu_mask & (1ULL << cpu)) {
- g_ipi_data[cpu].pending.fetch_or(reason, __ATOMIC_RELEASE);
- }
- }
-
- /* Deliver hardware interrupt via SBI.
- * RISC-V SBI IPI extension sends S-mode software interrupt
- * to the specified harts. hart_mask maps 1:1 to cpu_mask
- * (assuming hartid == cpu_id, which holds for QEMU virt). */
- arch::sbi::sbi_send_ipi(cpu_mask, 0);
-}
-
-/* ------------------------------------------------------------------ */
-/* smp_handle_ipi — called from trap handler on software interrupt */
-/* ------------------------------------------------------------------ */
-
-void smp_handle_ipi()
-{
- uint32 cpu = static_cast<uint32>(arch::cpu::id());
-
- /* Clear the S-mode software interrupt pending bit (sip.SSIP).
- * On RISC-V, we must clear this in software, otherwise the
- * interrupt will keep firing. */
- arch::cpu::clear_sip_ssip();
-
- /* Atomically consume all pending reasons */
- uint32 reasons = g_ipi_data[cpu].pending.exchange(0, __ATOMIC_ACQUIRE);
-
- if (reasons == 0) return; /* Spurious */
-
- /* Dispatch each reason */
-
- if (reasons & IPI_RESCHEDULE) {
- /* Just need to return from interrupt — schedule() will be
- * called on the return-from-trap path if need_resched is set.
- * For now, set the flag by yielding on next opportunity. */
- /* TODO: set need_resched flag when preemption tracking is added */
- }
-
- if (reasons & IPI_TLB_SHOOTDOWN) {
- /* Execute local TLB flush with the parameters the sender stored */
- uint64 start = g_ipi_data[cpu].tlb_start;
- uint64 end = g_ipi_data[cpu].tlb_end;
- if (start == 0 && end == 0) {
- arch::mmu::flush_tlb_all();
- } else {
- for (uint64 va = start; va < end; va += kPageSize) {
- arch::mmu::flush_tlb_page(va);
- }
- }
- }
-
- if (reasons & IPI_HALT) {
- printk(KERN_INFO "smp: CPU %u halting by IPI\n", cpu);
- arch::cpu::intr_off();
- while (true) {
- arch::cpu::halt_until_interrupt();
- }
- }
-}
diff --git a/na/kernel/sync/cpu_state.h b/na/kernel/sync/cpu_state.h
deleted file mode 100644
index 1f7dde0..0000000
--- a/na/kernel/sync/cpu_state.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Per-CPU Interrupt Nesting Tracker
- *
- * Tracks spinlock nesting depth on each CPU to ensure interrupts
- * are only restored when the last spinlock is released.
- *
- * BSS-segment global array; zero-initialized by entry.S BSS clear.
- * Reference: docs/specs/spinlock.md §3.1
- */
-
-#include <lume/types.h>
-#include <lume/config.h>
-
-namespace sync {
-
-struct CpuSyncState {
- int lock_depth; // Number of spinlocks held by this CPU
- bool intr_was_on; // Were interrupts enabled before the first lock?
-};
-
-inline constexpr uint64 kInvalidCpuId = ~0ULL;
-
-// BSS-segment global array, zero-initialized by entry.S
-extern CpuSyncState g_cpu_sync_states[kMaxCpus];
-
-// Internal API — called only by Spinlock::acquire/release
-void push_intr();
-void pop_intr();
-
-} // namespace sync
diff --git a/na/kernel/sync/mutex.cc b/na/kernel/sync/mutex.cc
deleted file mode 100644
index c4d3e9d..0000000
--- a/na/kernel/sync/mutex.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Mutex — Linux-style sleeping mutex (no PI).
- *
- * Implementation notes (mirroring kernel/locking/mutex.c, simplified):
- *
- * - Fast path (uncontended): wait_lock_ -> see owner_==null -> claim -> unlock.
- * - Slow path (contended): enqueue MutexWaiter on stack, sleep, race for
- * ownership after each wake. unlock() wakes only the head waiter.
- * - No optimistic spin, no handoff anti-starvation, no interruptible wait.
- * These belong to follow-up work (see mutex.md §5).
- *
- * Reference: docs/specs/mutex.md
- */
-
-#include <lume/mutex.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-#include <lume/klog.h>
-#include <arch/cpu.h>
-
-namespace {
-
-struct MutexWaiter {
- list_node link;
- TaskControlBlock* task;
-};
-
-} // namespace
-
-/* ------------------------------------------------------------------ */
-/* init */
-/* ------------------------------------------------------------------ */
-
-void Mutex::init(const char* name)
-{
- owner_ = nullptr;
- name_ = name;
- wait_lock_.init(name != nullptr ? name : "mutex");
- wait_list_.init();
-}
-
-/* ------------------------------------------------------------------ */
-/* try_lock — non-blocking, safe from any context */
-/* ------------------------------------------------------------------ */
-
-bool Mutex::try_lock()
-{
- LockGuard g(wait_lock_);
- if (owner_ != nullptr) {
- return false;
- }
- owner_ = current_task();
- return true;
-}
-
-/* ------------------------------------------------------------------ */
-/* lock — blocking, must be called in schedulable context */
-/* ------------------------------------------------------------------ */
-
-void Mutex::lock()
-{
- TaskControlBlock* curr = current_task();
-
- if (curr == nullptr) {
- kernel_panic("Mutex::lock: no current task");
- }
- if (curr->preempt_count > 0) {
- kernel_panic("Mutex::lock: preemption disabled (holding spinlock?)");
- }
-
- wait_lock_.acquire();
-
- if (owner_ == nullptr) {
- owner_ = curr;
- wait_lock_.release();
- return;
- }
-
- if (owner_ == curr) {
- wait_lock_.release();
- kernel_panic("Mutex::lock: recursive acquire by same task");
- }
-
- /* Slow path: enqueue ourselves and sleep until we are head + lock free. */
- MutexWaiter waiter;
- waiter.link.init();
- waiter.task = curr;
- list_add_tail(&waiter.link, &wait_list_);
-
- for (;;) {
- /* Set state inside wait_lock_ so unlock-side sees a coherent view:
- * either we are on the list AND Sleeping (will be woken), or we
- * already took ownership above (not in this loop). */
- curr->state = TaskState::Sleeping;
- wait_lock_.release();
-
- schedule();
-
- wait_lock_.acquire();
-
- if (owner_ == nullptr && wait_list_.next == &waiter.link) {
- owner_ = curr;
- list_del_init(&waiter.link);
- wait_lock_.release();
- return;
- }
- /* Spurious wake / lost race — keep waiting. */
- }
-}
-
-/* ------------------------------------------------------------------ */
-/* unlock */
-/* ------------------------------------------------------------------ */
-
-void Mutex::unlock()
-{
- TaskControlBlock* curr = current_task();
-
- wait_lock_.acquire();
-
- if (owner_ != curr) {
- wait_lock_.release();
- kernel_panic("Mutex::unlock: not the owner");
- }
-
- owner_ = nullptr;
-
- if (!wait_list_.is_empty()) {
- list_node* first = wait_list_.next;
- MutexWaiter* w = list_entry<MutexWaiter, &MutexWaiter::link>(first);
- TaskControlBlock* victim = w->task;
-
- /* Race-tolerant wake: only enqueue if still sleeping. If the
- * waiter is already Runnable (e.g. it was already woken by a
- * timer-driven schedule reentry and didn't yet retry the lock),
- * sched_enqueue would double-link it. */
- if (victim->state == TaskState::Sleeping) {
- victim->state = TaskState::Runnable;
- sched_enqueue(victim);
- }
- }
-
- wait_lock_.release();
-}
-
-/* ------------------------------------------------------------------ */
-/* Debug accessors */
-/* ------------------------------------------------------------------ */
-
-bool Mutex::is_owned_by_current() const
-{
- return owner_ == current_task();
-}
diff --git a/na/kernel/sync/spinlock.cc b/na/kernel/sync/spinlock.cc
deleted file mode 100644
index f649fe7..0000000
--- a/na/kernel/sync/spinlock.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Spinlock Implementation
- *
- * TTAS (Test-and-Test-and-Set) spinlock with:
- * - Acquire: __ATOMIC_ACQUIRE memory ordering
- * - Release: __ATOMIC_RELEASE memory ordering
- * - Automatic interrupt disable/enable via push_intr/pop_intr
- * - Dead-lock and invalid-release detection via kernel_panic
- *
- * Reference: docs/specs/spinlock.md
- */
-
-#include "spinlock.h"
-#include "cpu_state.h"
-#include <arch/cpu.h>
-#include <lume/klog.h>
-#include <lume/task.h>
-
-// ============================================================
-// Per-CPU sync state (BSS zero-initialized)
-// ============================================================
-namespace sync {
-
-CpuSyncState g_cpu_sync_states[kMaxCpus];
-
-void push_intr() {
- bool was_on = arch::cpu::intr_enabled();
- arch::cpu::intr_off();
-
- auto& state = g_cpu_sync_states[arch::cpu::id()];
- if (state.lock_depth == 0) {
- state.intr_was_on = was_on;
- }
- state.lock_depth++;
-}
-
-void pop_intr() {
- auto& state = g_cpu_sync_states[arch::cpu::id()];
- state.lock_depth--;
-
- if (state.lock_depth == 0 && state.intr_was_on) {
- arch::cpu::intr_on();
- }
-}
-
-} // namespace sync
-
-// ============================================================
-// Spinlock methods
-// ============================================================
-
-void Spinlock::init(const char* name) {
- name_ = name;
- locked_.store(0, __ATOMIC_RELAXED);
- cpu_id_ = sync::kInvalidCpuId;
-}
-
-void Spinlock::acquire() {
-#ifdef DEBUG
- // Optional: detect interrupt-state leakage
- if (arch::cpu::intr_enabled()) {
- auto& state = sync::g_cpu_sync_states[arch::cpu::id()];
- if (state.lock_depth > 0) {
- kernel_panic("spinlock: interrupts enabled while holding lock");
- }
- }
-#endif
-
- // Step 1: disable interrupts, track nesting depth
- sync::push_intr();
-
- // Step 2: dead-lock detection (same CPU re-acquiring)
- if (is_held_by_current_cpu()) {
- kernel_panic("spinlock: double lock", name_);
- }
-
- // Step 3: TTAS spin loop
- while (true) {
- // Outer: spin on local cache copy (no bus traffic)
- while (locked_.load(__ATOMIC_RELAXED))
- ;
- // Inner: attempt atomic exchange with acquire semantics
- if (locked_.exchange(1, __ATOMIC_ACQUIRE) == 0)
- break;
- }
-
- // Step 4: record owner
- cpu_id_ = arch::cpu::id();
-
- // Step 5: bump preempt_count — prevents schedule() from preempting
- TaskControlBlock* curr = current_task();
- if (curr) curr->preempt_count++;
-}
-
-void Spinlock::release() {
- // Step 1: verify caller is the holder
- if (!is_held_by_current_cpu()) {
- kernel_panic("spinlock: invalid release", name_);
- }
-
- // Step 2: clear owner
- cpu_id_ = sync::kInvalidCpuId;
-
- // Step 3: release the lock with release semantics
- locked_.store(0, __ATOMIC_RELEASE);
-
- // Step 4: drop preempt_count
- TaskControlBlock* curr = current_task();
- if (curr) curr->preempt_count--;
-
- // Step 5: restore interrupt state if this was the last lock
- sync::pop_intr();
-}
-
-bool Spinlock::is_held_by_current_cpu() const {
- return cpu_id_ == arch::cpu::id();
-}
diff --git a/na/kernel/sync/spinlock.h b/na/kernel/sync/spinlock.h
deleted file mode 100644
index a1369d1..0000000
--- a/na/kernel/sync/spinlock.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * Spinlock & LockGuard
- *
- * Provides mutual exclusion for SMP kernels using TTAS
- * (Test-and-Test-and-Set) with acquire/release memory ordering.
- * Interrupt disable/enable is managed automatically via push_intr/pop_intr.
- *
- * Reference: docs/specs/spinlock.md
- */
-
-#include <lume/atomic.h>
-#include <lume/types.h>
-#include "cpu_state.h"
-
-class Spinlock {
-public:
- // Default: BSS zero is safe. Call init() to give the lock a name.
- Spinlock() = default;
-
- // Explicit initialization (for global objects or Placement New)
- void init(const char* name);
-
- // Non-copyable, non-movable (lock identity is its address)
- Spinlock(const Spinlock&) = delete;
- Spinlock& operator=(const Spinlock&) = delete;
- Spinlock(Spinlock&&) = delete;
- Spinlock& operator=(Spinlock&&) = delete;
-
- void acquire();
- void release();
- bool is_held_by_current_cpu() const;
-
-private:
- lume::atomic<uint32> locked_; // 0 = free, 1 = held
- const char* name_ = "uninit"; // Lock name (for panic messages)
- uint64 cpu_id_ = sync::kInvalidCpuId; // Holder CPU ID
-};
-
-class LockGuard {
-public:
- explicit LockGuard(Spinlock& lock) : lock_(lock) {
- lock_.acquire();
- }
-
- ~LockGuard() {
- lock_.release();
- }
-
- LockGuard(const LockGuard&) = delete;
- LockGuard& operator=(const LockGuard&) = delete;
- LockGuard(LockGuard&&) = delete;
- LockGuard& operator=(LockGuard&&) = delete;
-
-private:
- Spinlock& lock_;
-};
diff --git a/na/kernel/sys_fs.cc b/na/kernel/sys_fs.cc
deleted file mode 100644
index 1d26fdd..0000000
--- a/na/kernel/sys_fs.cc
+++ /dev/null
@@ -1,679 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * sys_fs.cc — Filesystem-related syscall handlers
- *
- * Implements: openat, close, read, write, lseek, fstat, getdents64,
- * dup, dup3, getcwd, chdir, mkdirat, unlinkat
- */
-
-#include <lume/syscall.h>
-#include <lume/syscall_nr.h>
-#include <lume/errno.h>
-#include <lume/console.h>
-#include <lume/task.h>
-#include <lume/fs.h>
-#include <lume/file.h>
-#include <lume/dcache.h>
-#include <lume/namei.h>
-#include <lume/fdtable.h>
-#include <lume/fs_struct.h>
-#include <lume/mount.h>
-#include <lume/fs_types.h>
-#include <lume/klog.h>
-
-/* ================================================================== */
-/* Helpers */
-/* ================================================================== */
-
-static files_struct* current_files()
-{
- TaskControlBlock* t = current_task();
- return t ? t->files : nullptr;
-}
-
-static fs_struct* current_fs()
-{
- TaskControlBlock* t = current_task();
- return t ? t->fs : nullptr;
-}
-
-/* ================================================================== */
-/* sys_openat(dirfd, pathname, flags, mode) */
-/* ================================================================== */
-
-int64 sys_openat(uint64 a0, uint64 a1, uint64 a2,
- uint64 a3, uint64 /*a4*/, uint64 /*a5*/)
-{
- int dirfd = static_cast<int>(a0);
- const char* pathname = reinterpret_cast<const char*>(a1);
- int flags = static_cast<int>(a2);
- uint32 mode = static_cast<uint32>(a3);
-
- files_struct* files = current_files();
- if (!files) return -EMFILE;
-
-
- /* Pathname lookup */
- path p{};
- int err = filename_lookup(dirfd, pathname, 0, &p);
-
- if (err == -ENOENT && (flags & O_CREAT)) {
- /* Create: look up the parent directory */
- /* Find last '/' to split parent path / filename */
- const char* last_slash = nullptr;
- for (const char* s = pathname; *s; s++) {
- if (*s == '/') last_slash = s;
- }
-
- if (!last_slash || last_slash == pathname) {
- /* File in root or current directory */
- path parent{};
- if (last_slash == pathname) {
- /* Absolute: parent is root */
- parent.de = g_root_dentry;
- parent.mnt = g_root_mnt;
- } else {
- /* Relative: parent is cwd */
- fs_struct* fs = current_fs();
- if (!fs) return -ENOENT;
- parent = fs->pwd;
- }
-
- const char* name = last_slash ? last_slash + 1 : pathname;
- inode* dir_ip = parent.de->d_inode;
- if (!dir_ip || !dir_ip->i_op || !dir_ip->i_op->create) {
- return -EACCES;
- }
-
- /* Create the inode via the directory's i_op->create */
- qstr q{};
- q.name = name;
- q.len = 0;
- for (const char* s = name; *s; s++) q.len++;
- dentry* new_de = d_alloc(parent.de, &q);
- if (!new_de) return -ENOMEM;
-
-
- err = dir_ip->i_op->create(dir_ip, new_de, mode & ~S_IFMT, false);
- if (err) {
- /* TODO: free new_de */
- return err;
- }
-
- p.de = new_de;
- p.mnt = parent.mnt;
- } else {
- /* TODO: multi-component parent lookup */
- return -ENOENT;
- }
- } else if (err) {
- return err;
- }
-
- /* Allocate fd */
- int fd = alloc_fd(files, 0);
- if (fd < 0) {
- return fd;
- }
-
- /* Allocate file object */
- file* fp = alloc_empty_file(flags);
- if (!fp) {
- /* TODO: release fd */
- return -ENFILE;
- }
-
- fp->f_inode = p.de->d_inode;
- fp->f_path = p;
- fp->f_op = p.de->d_inode->i_fop;
- fp->f_pos = 0;
-
- /* Chardev: look up cdev by i_rdev and replace f_op */
- if (S_ISCHR(fp->f_inode->i_mode)) {
- extern int devfs_chrdev_open(inode* ip, file* fp);
- err = devfs_chrdev_open(fp->f_inode, fp);
- if (err) {
- fput(fp);
- return err;
- }
- } else if (fp->f_op && fp->f_op->open) {
- err = fp->f_op->open(fp->f_inode, fp);
- if (err) {
- fput(fp);
- return err;
- }
- }
-
- /* O_TRUNC for regular files */
- if ((flags & O_TRUNC) && S_ISREG(fp->f_inode->i_mode)) {
- fp->f_inode->i_size = 0;
- }
-
- fd_install(files, fd, fp);
- return fd;
-}
-
-/* ================================================================== */
-/* sys_close(fd) */
-/* ================================================================== */
-
-int64 sys_close(uint64 a0, uint64 /*a1*/, uint64 /*a2*/,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- files_struct* files = current_files();
- if (!files) return -EBADF;
- return close_fd(files, fd);
-}
-
-/* ================================================================== */
-/* sys_read(fd, buf, count) */
-/* ================================================================== */
-
-int64 sys_read(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- char* buf = reinterpret_cast<char*>(a1);
- uint64 count = a2;
-
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- file* fp = fcheck(files, fd);
- if (!fp) return -EBADF;
-
- if (!(fp->f_mode & FMODE_READ)) return -EBADF;
-
- return vfs_read(fp, buf, count, &fp->f_pos);
-}
-
-/* ================================================================== */
-/* sys_write(fd, buf, count) */
-/* ================================================================== */
-
-int64 sys_write(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- const char* buf = reinterpret_cast<const char*>(a1);
- uint64 count = a2;
-
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- file* fp = fcheck(files, fd);
- if (!fp) return -EBADF;
-
- if (!(fp->f_mode & FMODE_WRITE)) return -EBADF;
-
- return vfs_write(fp, buf, count, &fp->f_pos);
-}
-
-/* ================================================================== */
-/* sys_lseek(fd, offset, whence) */
-/* ================================================================== */
-
-int64 sys_lseek(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- int64 offset = static_cast<int64>(a1);
- int whence = static_cast<int>(a2);
-
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- file* fp = fcheck(files, fd);
- if (!fp) return -EBADF;
-
- return vfs_llseek(fp, offset, whence);
-}
-
-/* ================================================================== */
-/* sys_fstat(fd, statbuf) */
-/* ================================================================== */
-
-int64 sys_fstat_wrapper(uint64 a0, uint64 a1, uint64 /*a2*/,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- auto* statbuf = reinterpret_cast<kstat*>(a1);
-
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- file* fp = fcheck(files, fd);
- if (!fp) return -EBADF;
-
- return vfs_fstat(fp, statbuf);
-}
-
-/* ================================================================== */
-/* sys_getdents64(fd, dirp, count) */
-/* ================================================================== */
-
-struct linux_dirent64 {
- uint64 d_ino;
- int64 d_off;
- uint16 d_reclen;
- uint8 d_type;
- char d_name[];
-};
-
-struct getdents_ctx {
- dir_context ctx;
- char* buf;
- int count;
- int written;
- int error;
-};
-
-static bool filldir64(dir_context* ctx, const char* name, int namelen,
- int64 offset, uint64 ino, unsigned int d_type)
-{
- auto* gctx = container_of(ctx, getdents_ctx, ctx);
-
- /* Calculate record length (rounded up to 8 bytes) */
- int reclen = static_cast<int>(
- (sizeof(linux_dirent64) + namelen + 1 + 7) & ~7ULL);
-
- if (gctx->written + reclen > gctx->count) {
- return false; /* Buffer full */
- }
-
- auto* dirent = reinterpret_cast<linux_dirent64*>(
- gctx->buf + gctx->written);
-
- dirent->d_ino = ino;
- dirent->d_off = offset + 1;
- dirent->d_reclen = static_cast<uint16>(reclen);
- dirent->d_type = static_cast<uint8>(d_type);
-
- /* Copy name */
- for (int i = 0; i < namelen; i++) {
- dirent->d_name[i] = name[i];
- }
- dirent->d_name[namelen] = '\0';
-
- gctx->written += reclen;
- return true;
-}
-
-int64 sys_getdents64(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- char* buf = reinterpret_cast<char*>(a1);
- int count = static_cast<int>(a2);
-
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- file* fp = fcheck(files, fd);
- if (!fp) return -EBADF;
-
- if (!fp->f_op || !fp->f_op->iterate_shared) {
- return -ENOTDIR;
- }
-
- getdents_ctx gctx{};
- gctx.ctx.actor = filldir64;
- gctx.ctx.pos = fp->f_pos;
- gctx.buf = buf;
- gctx.count = count;
- gctx.written = 0;
- gctx.error = 0;
-
- fp->f_op->iterate_shared(fp, &gctx.ctx);
- fp->f_pos = gctx.ctx.pos;
-
- return gctx.written;
-}
-
-/* ================================================================== */
-/* sys_dup(oldfd) */
-/* ================================================================== */
-
-int64 sys_dup(uint64 a0, uint64 /*a1*/, uint64 /*a2*/,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int oldfd = static_cast<int>(a0);
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- file* fp = fcheck(files, oldfd);
- if (!fp) return -EBADF;
-
- int newfd = alloc_fd(files, 0);
- if (newfd < 0) return newfd;
-
- get_file(fp);
- fd_install(files, newfd, fp);
- return newfd;
-}
-
-/* ================================================================== */
-/* sys_dup3(oldfd, newfd, flags) */
-/* ================================================================== */
-
-int64 sys_dup3(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int oldfd = static_cast<int>(a0);
- int newfd = static_cast<int>(a1);
- int flags = static_cast<int>(a2);
-
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- return do_dup2(files, oldfd, newfd, flags);
-}
-
-/* ================================================================== */
-/* sys_getcwd(buf, size) */
-/* ================================================================== */
-
-int64 sys_getcwd(uint64 a0, uint64 a1, uint64 /*a2*/,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- char* buf = reinterpret_cast<char*>(a0);
- uint64 size = a1;
-
- if (!buf || size == 0) return -EINVAL;
-
- fs_struct* fs = current_fs();
- if (!fs) return -ENOENT;
-
- char tmp[256];
- char* result = d_path(&fs->pwd, tmp, sizeof(tmp));
- if (!result) return -ERANGE;
-
- /* Calculate length */
- int len = 0;
- for (const char* s = result; *s; s++) len++;
- len++; /* Include NUL */
-
- if (static_cast<uint64>(len) > size) return -ERANGE;
-
- for (int i = 0; i < len; i++) {
- buf[i] = result[i];
- }
-
- return len;
-}
-
-/* ================================================================== */
-/* sys_chdir(pathname) */
-/* ================================================================== */
-
-int64 sys_chdir(uint64 a0, uint64 /*a1*/, uint64 /*a2*/,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- const char* pathname = reinterpret_cast<const char*>(a0);
-
- path p{};
- int err = filename_lookup(AT_FDCWD, pathname, LOOKUP_DIRECTORY, &p);
- if (err) return err;
-
- if (!S_ISDIR(p.de->d_inode->i_mode)) {
- path_put(&p);
- return -ENOTDIR;
- }
-
- fs_struct* fs = current_fs();
- if (!fs) {
- path_put(&p);
- return -ENOENT;
- }
-
- set_fs_pwd(fs, &p);
- path_put(&p);
- return 0;
-}
-
-/* ================================================================== */
-/* sys_mkdirat(dirfd, pathname, mode) */
-/* ================================================================== */
-
-int64 sys_mkdirat(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int dirfd = static_cast<int>(a0);
- const char* pathname = reinterpret_cast<const char*>(a1);
- uint32 mode = static_cast<uint32>(a2);
-
- /* Walk to the parent directory */
- /* For simplicity, support only "single component" paths for now */
- path parent{};
- int err;
-
- if (pathname[0] == '/') {
- parent.de = g_root_dentry;
- parent.mnt = g_root_mnt;
- pathname++; /* skip leading / */
- } else {
- fs_struct* fs = current_fs();
- if (!fs) return -ENOENT;
- parent = fs->pwd;
- (void)dirfd; /* TODO: respect dirfd for AT_FDCWD check */
- }
-
- inode* dir_ip = parent.de->d_inode;
- if (!dir_ip || !dir_ip->i_op || !dir_ip->i_op->mkdir) {
- return -EPERM;
- }
-
- qstr q{};
- q.name = pathname;
- q.len = 0;
- for (const char* s = pathname; *s; s++) q.len++;
- dentry* new_de = d_alloc(parent.de, &q);
- if (!new_de) return -ENOMEM;
-
- err = dir_ip->i_op->mkdir(dir_ip, new_de, mode | S_IFDIR);
- if (err) {
- return err;
- }
-
- return 0;
-}
-
-/* ================================================================== */
-/* sys_unlinkat(dirfd, pathname, flags) */
-/* ================================================================== */
-
-int64 sys_unlinkat(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- (void)a0; (void)a1; (void)a2;
- /* TODO: implement unlink/rmdir */
- return -ENOSYS;
-}
-
-/* ================================================================== */
-/* sys_writev(fd, iov, iovcnt) */
-/* ================================================================== */
-
-struct iovec {
- void* iov_base;
- uint64 iov_len;
-};
-
-int64 sys_writev(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- const iovec* iov = reinterpret_cast<const iovec*>(a1);
- int iovcnt = static_cast<int>(a2);
-
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- file* fp = fcheck(files, fd);
- if (!fp) return -EBADF;
- if (!(fp->f_mode & FMODE_WRITE)) return -EBADF;
-
- int64 total = 0;
- for (int i = 0; i < iovcnt; i++) {
- if (iov[i].iov_len == 0) continue;
- int64 ret = vfs_write(fp, static_cast<const char*>(iov[i].iov_base),
- iov[i].iov_len, &fp->f_pos);
- if (ret < 0) {
- return total > 0 ? total : ret;
- }
- total += ret;
- if (static_cast<uint64>(ret) < iov[i].iov_len) break;
- }
- return total;
-}
-
-/* ================================================================== */
-/* sys_ioctl — dispatch to file_operations */
-/* ================================================================== */
-
-int64 sys_ioctl(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- unsigned int cmd = static_cast<unsigned int>(a1);
- uint64 arg = a2;
-
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->files) return -EBADF;
-
- file* fp = fcheck(curr->files, fd);
- if (!fp) return -EBADF;
-
- if (fp->f_op && fp->f_op->unlocked_ioctl)
- return fp->f_op->unlocked_ioctl(fp, cmd, arg);
-
- return -ENOTTY;
-}
-
-/* ================================================================== */
-/* sys_pipe2(fds, flags) */
-/* ================================================================== */
-
-#include <lume/pipe.h>
-
-int64 sys_pipe2(uint64 a0, uint64 a1, uint64 /*a2*/,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int* user_fds = reinterpret_cast<int*>(a0);
- int flags = static_cast<int>(a1);
-
- files_struct* files = current_files();
- if (!files) return -EMFILE;
-
- int fds[2];
- int err = do_pipe2(files, fds, flags);
- if (err) return err;
-
- user_fds[0] = fds[0];
- user_fds[1] = fds[1];
- return 0;
-}
-
-/* ================================================================== */
-/* sys_fcntl(fd, cmd, arg) */
-/* ================================================================== */
-
-/* fcntl commands — Linux ABI values */
-inline constexpr int F_DUPFD = 0;
-inline constexpr int F_GETFD = 1;
-inline constexpr int F_SETFD = 2;
-inline constexpr int F_GETFL = 3;
-inline constexpr int F_SETFL = 4;
-inline constexpr int F_DUPFD_CLOEXEC = 1030;
-
-inline constexpr int FD_CLOEXEC = 1;
-
-/* Bitmap helpers (duplicated from fd.cc — TODO: move to shared header) */
-static inline bool fcntl_bit_test(const uint64* bmap, int bit) {
- return (bmap[bit / 64] >> (bit % 64)) & 1;
-}
-static inline void fcntl_bit_set(uint64* bmap, int bit) {
- bmap[bit / 64] |= (1ULL << (bit % 64));
-}
-static inline void fcntl_bit_clear(uint64* bmap, int bit) {
- bmap[bit / 64] &= ~(1ULL << (bit % 64));
-}
-
-int64 sys_fcntl(uint64 a0, uint64 a1, uint64 a2,
- uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
-{
- int fd = static_cast<int>(a0);
- int cmd = static_cast<int>(a1);
- uint64 arg = a2;
-
- files_struct* files = current_files();
- if (!files) return -EBADF;
-
- file* fp = fcheck(files, fd);
- if (!fp) return -EBADF;
-
- switch (cmd) {
- case F_DUPFD:
- case F_DUPFD_CLOEXEC: {
- int new_fd = alloc_fd(files, static_cast<unsigned int>(arg));
- if (new_fd < 0) return new_fd;
- get_file(fp);
- fd_install(files, new_fd, fp);
- if (cmd == F_DUPFD_CLOEXEC) {
- LockGuard guard(files->file_lock);
- fcntl_bit_set(files->close_on_exec_init, new_fd);
- }
- return new_fd;
- }
-
- case F_GETFD: {
- LockGuard guard(files->file_lock);
- return fcntl_bit_test(files->close_on_exec_init, fd) ? FD_CLOEXEC : 0;
- }
-
- case F_SETFD: {
- LockGuard guard(files->file_lock);
- if (arg & FD_CLOEXEC)
- fcntl_bit_set(files->close_on_exec_init, fd);
- else
- fcntl_bit_clear(files->close_on_exec_init, fd);
- return 0;
- }
-
- case F_GETFL:
- return fp->f_flags;
-
- case F_SETFL:
- /* Only allow changing O_APPEND, O_NONBLOCK, O_ASYNC */
- fp->f_flags = (fp->f_flags & ~(O_APPEND | O_NONBLOCK)) |
- (static_cast<unsigned int>(arg) & (O_APPEND | O_NONBLOCK));
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-/* ================================================================== */
-/* sys_socket (SYS_socket = 198) & sys_sendto (SYS_sendto = 206) */
-/* ================================================================== */
-
-int64 sys_socket(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- /*
- * COMPATIBILITY STUB:
- * LumeOS currently lacks a networking stack and Unix Domain Sockets (AF_UNIX).
- * We return -EAFNOSUPPORT (Address family not supported by protocol) as expected
- * by Linux applications (like BusyBox syslogd connecting to /dev/log).
- */
- return -EAFNOSUPPORT;
-}
-
-int64 sys_sendto(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- /* COMPATIBILITY STUB */
- return -EAFNOSUPPORT;
-}
diff --git a/na/kernel/sys_info.cc b/na/kernel/sys_info.cc
deleted file mode 100644
index 79baab0..0000000
--- a/na/kernel/sys_info.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * System Information Calls
- *
- * Implements: uname
- *
- * Reference: docs/specs/syscall.md §7.3
- */
-
-#include <lume/types.h>
-#include <lume/errno.h>
-#include <lume/uaccess.h>
-
-/* Linux-compatible utsname structure (65-byte fields) */
-struct utsname {
- char sysname[65];
- char nodename[65];
- char release[65];
- char version[65];
- char machine[65];
- char domainname[65];
-};
-
-/* Simple safe string copy */
-static void safe_strncpy(char* dst, const char* src, uint64 n)
-{
- uint64 i = 0;
- for (; i < n - 1 && src[i] != '\0'; i++)
- dst[i] = src[i];
- dst[i] = '\0';
-}
-
-int64 sys_uname(uint64 buf_ptr, uint64, uint64, uint64, uint64, uint64)
-{
- utsname info = {};
- safe_strncpy(info.sysname, "LumeOS", sizeof(info.sysname));
- safe_strncpy(info.nodename, "lume", sizeof(info.nodename));
- safe_strncpy(info.release, "0.1.0", sizeof(info.release));
- safe_strncpy(info.version, "Phase 2", sizeof(info.version));
- safe_strncpy(info.machine, "riscv64", sizeof(info.machine));
- safe_strncpy(info.domainname, "(none)", sizeof(info.domainname));
-
- if (copy_to_user(buf_ptr, &info, sizeof(info)) < 0)
- return -EFAULT;
-
- return 0;
-}
diff --git a/na/kernel/sys_misc.cc b/na/kernel/sys_misc.cc
deleted file mode 100644
index 48eb47b..0000000
--- a/na/kernel/sys_misc.cc
+++ /dev/null
@@ -1,360 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Miscellaneous System Calls — busybox bootstrap set
- *
- * Each syscall follows the Linux kernel semantics exactly.
- * Reference: Linux kernel/sys.c, kernel/signal.c, kernel/time/posix-timers.c
- */
-
-#include <lume/types.h>
-#include <lume/task.h>
-#include <lume/errno.h>
-#include <lume/kprintf.h>
-#include <lume/klog.h>
-#include <lume/pid.h>
-#include <lume/pgrp.h>
-#include <lume/signal_types.h>
-#include <lume/tty.h>
-#include <lume/uaccess.h>
-#include <arch/cpu.h>
-
-/* ======================================================================
- * sys_reboot — Linux kernel/reboot.c
- *
- * Validates magic numbers and performs the requested action.
- * For now, we only support LINUX_REBOOT_CMD_CAD_ON/OFF (ignore)
- * and reject everything else with -EPERM (no CAP_SYS_BOOT).
- * ====================================================================== */
-
-static constexpr uint32 LINUX_REBOOT_MAGIC1 = 0xfee1dead;
-static constexpr uint32 LINUX_REBOOT_MAGIC2 = 672274793; /* 0x28121969 */
-static constexpr uint32 LINUX_REBOOT_MAGIC2A = 85072278;
-static constexpr uint32 LINUX_REBOOT_MAGIC2B = 369367448;
-static constexpr uint32 LINUX_REBOOT_MAGIC2C = 537993216;
-
-int64 sys_reboot(uint64 magic1, uint64 magic2, uint64 cmd,
- uint64, uint64, uint64)
-{
- /* Linux: kernel/reboot.c — validate magic numbers first */
- if (static_cast<uint32>(magic1) != LINUX_REBOOT_MAGIC1)
- return -EINVAL;
- if (static_cast<uint32>(magic2) != LINUX_REBOOT_MAGIC2 &&
- static_cast<uint32>(magic2) != LINUX_REBOOT_MAGIC2A &&
- static_cast<uint32>(magic2) != LINUX_REBOOT_MAGIC2B &&
- static_cast<uint32>(magic2) != LINUX_REBOOT_MAGIC2C)
- return -EINVAL;
-
- /* We don't have CAP_SYS_BOOT yet — reject with -EPERM.
- * This matches Linux behavior for unprivileged callers.
- * Busybox handles this gracefully and continues. */
- (void)cmd;
- printk(KERN_INFO "reboot: cmd=%llu (not implemented, returning -EPERM)\n", cmd);
- return -EPERM;
-}
-
-/* ======================================================================
- * sys_prctl — Linux kernel/sys.c
- *
- * Process control. Most options are optional for early boot.
- * We implement the minimum set busybox needs.
- * ====================================================================== */
-
-/* prctl option constants (from <linux/prctl.h>) */
-static constexpr int PR_SET_NAME = 15;
-static constexpr int PR_GET_NAME = 16;
-static constexpr int PR_SET_PDEATHSIG = 1;
-static constexpr int PR_GET_PDEATHSIG = 2;
-
-int64 sys_prctl(uint64 option, uint64 arg2, uint64 arg3,
- uint64 arg4, uint64 arg5, uint64)
-{
- switch (static_cast<int>(option)) {
- case PR_SET_PDEATHSIG:
- /* Linux: set signal delivered when parent dies.
- * Silently accept — we don't track pdeath_signal yet. */
- return 0;
- case PR_GET_PDEATHSIG: {
- /* Linux: return current pdeath_signal (0 = none) */
- int32 zero = 0;
- if (copy_to_user(arg2, &zero, sizeof(zero)) != 0)
- return -EFAULT;
- return 0;
- }
- case PR_SET_NAME: {
- /* Linux: set comm (task name), max 16 chars */
- /* TODO: copy_from_user the string */
- return 0;
- }
- case PR_GET_NAME: {
- /* Linux: get comm (task name) */
- TaskControlBlock* curr = current_task();
- if (!curr) return -ESRCH;
- const char* name = curr->name ? curr->name : "";
- uint64 len = 0;
- while (name[len] && len < 15) len++;
- char buf[16] = {};
- for (uint64 i = 0; i < len; i++) buf[i] = name[i];
- if (copy_to_user(arg2, buf, 16) != 0)
- return -EFAULT;
- return 0;
- }
- default:
- /* Unknown prctl — Linux returns -EINVAL for unrecognized options */
- return -EINVAL;
- }
-}
-
-/* ======================================================================
- * Session / Process-Group syscalls
- *
- * All four operate on the caller's signal_struct (or a target task's).
- * Identity lives in signal_struct, so these become straightforward reads
- * and pgrp-hash updates. We mirror Linux kernel/sys.c semantics closely.
- * ====================================================================== */
-
-static TaskControlBlock* find_task_by_pid(int32 pid)
-{
- extern list_node g_all_tasks;
- extern Spinlock g_task_list_lock;
- LockGuard guard(g_task_list_lock);
- list_node* node = g_all_tasks.next;
- while (node != &g_all_tasks) {
- auto* task = list_entry<TaskControlBlock, &TaskControlBlock::all_link>(node);
- if (task->pid == pid) return task;
- node = node->next;
- }
- return nullptr;
-}
-
-/*
- * sys_setsid — Linux kernel/sys.c:ksys_setsid()
- *
- * Caller becomes leader of a new session+pgrp. Refused if a pgrp with
- * id == caller pid already exists with members other than the caller —
- * that's Linux's "process group id already exists" check, which prevents
- * collisions when a non-leader tries to setsid.
- */
-int64 sys_setsid(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return -ESRCH;
-
- /* Detect collision: another pgrp already uses our pid as pgid. */
- bool conflict = false;
- for_each_pgrp_member(curr->pid, [&](signal_struct* sig) {
- if (sig != curr->signal) conflict = true;
- });
- if (conflict) return -EPERM;
-
- /* Move to new pgrp (creates one if missing), update sid, drop ctty. */
- change_pgid(curr->signal, curr->pid);
-
- /* Detach the controlling terminal. If this task is the session
- * leader that owns the tty (tty->session == caller's old sid),
- * also clear the tty's session/pgrp so the next session-leader
- * that opens the device can claim it. Without this, tty->session
- * permanently sticks to the abandoned sid and every subsequent
- * tty_acquire_ctty() bails with "owned by another session" —
- * which is exactly what kept BusyBox's child sh from picking the
- * console back up after init's setsid().
- *
- * Linux's behavior is similar in spirit (disassociate_ctty when
- * the leader exits), but it triggers from exit, not setsid. Our
- * simpler "clear-on-give-up" matches the way our acquisition path
- * is written. */
- tty_struct* old_tty = nullptr;
- int32 old_sid = 0;
- {
- LockGuard guard(curr->signal->lock);
- old_tty = curr->signal->tty;
- old_sid = curr->signal->sid;
- curr->signal->sid = curr->pid;
- curr->signal->tty = nullptr;
- curr->signal->tty_old_pgrp = 0;
- }
- if (old_tty) {
- LockGuard guard(old_tty->lock);
- if (old_tty->session == old_sid) {
- old_tty->session = 0;
- old_tty->pgrp = 0;
- }
- }
- return curr->pid;
-}
-
-/*
- * sys_setpgid — Linux kernel/sys.c:ksys_setpgid()
- *
- * Move a process between groups within the same session. Allowed:
- * - caller targeting self or a child
- * - target is not a session leader
- * - target stays in caller's session
- * - target hasn't exec()'d (we don't track did_exec yet, so skip)
- * - new pgid is either == target's pid (new group) or names a pgrp
- * that already exists in the same session
- */
-int64 sys_setpgid(uint64 pid, uint64 pgid, uint64, uint64, uint64, uint64)
-{
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return -ESRCH;
-
- int32 target_pid = static_cast<int32>(pid);
- int32 target_pgid = static_cast<int32>(pgid);
-
- if (target_pid == 0) target_pid = curr->pid;
- if (target_pgid == 0) target_pgid = target_pid;
- if (target_pgid < 0) return -EINVAL;
-
- TaskControlBlock* target = (target_pid == curr->pid)
- ? curr
- : find_task_by_pid(target_pid);
- if (!target || !target->signal) return -ESRCH;
-
- /* Target must be caller or a child of caller. */
- if (target != curr && target->parent != curr) return -ESRCH;
-
- /* Same session as caller. */
- if (target->signal->sid != curr->signal->sid) return -EPERM;
-
- /* Cannot move a session leader into a different pgrp. */
- if (target->signal->sid == target->pid) return -EPERM;
-
- /* If target_pgid != target_pid, the pgrp must already exist in
- * the same session. pgrp existence == any signal_struct with
- * pgid == target_pgid AND sid == caller's sid. */
- if (target_pgid != target->pid) {
- bool found = false;
- for_each_pgrp_member(target_pgid, [&](signal_struct* sig) {
- if (sig->sid == curr->signal->sid) found = true;
- });
- if (!found) return -EPERM;
- }
-
- change_pgid(target->signal, target_pgid);
- return 0;
-}
-
-/*
- * sys_getpgid — return process group ID of `pid` (0 = self).
- */
-int64 sys_getpgid(uint64 pid, uint64, uint64, uint64, uint64, uint64)
-{
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return -ESRCH;
-
- if (pid == 0) return curr->signal->pgid;
-
- TaskControlBlock* target = find_task_by_pid(static_cast<int32>(pid));
- if (!target || !target->signal) return -ESRCH;
- return target->signal->pgid;
-}
-
-/*
- * sys_getsid — return session ID of `pid` (0 = self).
- */
-int64 sys_getsid(uint64 pid, uint64, uint64, uint64, uint64, uint64)
-{
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->signal) return -ESRCH;
-
- if (pid == 0) return curr->signal->sid;
-
- TaskControlBlock* target = find_task_by_pid(static_cast<int32>(pid));
- if (!target || !target->signal) return -ESRCH;
-
- /* Linux: -EPERM if target's session differs from caller's and we're
- * in a different namespace. We have one namespace, so always allow. */
- return target->signal->sid;
-}
-
-/* ======================================================================
- * sys_geteuid / sys_getegid — Linux kernel/sys.c
- *
- * Return effective UID/GID. We run everything as root (0).
- * ====================================================================== */
-
-int64 sys_geteuid(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- /* Linux: return current_euid(). We are always root. */
- return 0;
-}
-
-int64 sys_getegid(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- /* Linux: return current_egid(). We are always root. */
- return 0;
-}
-
-/* ======================================================================
- * sys_sigaltstack — Linux kernel/signal.c
- *
- * Set/get alternate signal stack. For now, accept and ignore
- * (busybox doesn't critically need it for shell operation).
- * ====================================================================== */
-
-int64 sys_sigaltstack(uint64 uss_ptr, uint64 uoss_ptr,
- uint64, uint64, uint64, uint64)
-{
- /* Linux: if uoss != NULL, return current alt stack info.
- * We don't have alt stacks yet — report SS_DISABLE. */
- if (uoss_ptr != 0) {
- /* stack_t { void* ss_sp; int ss_flags; size_t ss_size; }
- * On RISC-V LP64: 8 + 4 + padding + 8 = 24 bytes typically,
- * but the ABI packs it as {uint64, int32, pad, uint64}. */
- struct {
- uint64 ss_sp;
- int32 ss_flags;
- int32 _pad;
- uint64 ss_size;
- } oss = { 0, 2 /* SS_DISABLE */, 0, 0 };
- if (copy_to_user(uoss_ptr, &oss, sizeof(oss)) != 0)
- return -EFAULT;
- }
-
- /* uss: silently accept (we'll need it for proper signal handling later) */
- (void)uss_ptr;
- return 0;
-}
-
-/* ======================================================================
- * sys_clock_gettime — Linux kernel/time/posix-timers.c
- *
- * Get the current time for a given clock.
- * We use the RISC-V cycle counter as a monotonic source.
- * ====================================================================== */
-
-static constexpr int CLOCK_REALTIME = 0;
-static constexpr int CLOCK_MONOTONIC = 1;
-
-int64 sys_clock_gettime(uint64 clk_id, uint64 tp_ptr,
- uint64, uint64, uint64, uint64)
-{
- if (tp_ptr == 0) return -EFAULT;
-
- /* Read RISC-V time CSR (mtime mapped to SBI) */
- uint64 ticks = arch::cpu::read_time();
-
- /* Convert to seconds + nanoseconds.
- * QEMU virt timebase-frequency is 10MHz. */
- constexpr uint64 kTimebaseHz = 10'000'000ULL;
- uint64 sec = ticks / kTimebaseHz;
- uint64 nsec = (ticks % kTimebaseHz) * (1'000'000'000ULL / kTimebaseHz);
-
- struct timespec64 {
- int64 tv_sec;
- int64 tv_nsec;
- };
-
- timespec64 ts;
- ts.tv_sec = static_cast<int64>(sec);
- ts.tv_nsec = static_cast<int64>(nsec);
-
- (void)clk_id; /* We treat all clocks as monotonic for now */
-
- if (copy_to_user(tp_ptr, &ts, sizeof(ts)) != 0)
- return -EFAULT;
-
- return 0;
-}
diff --git a/na/kernel/sys_mm.cc b/na/kernel/sys_mm.cc
deleted file mode 100644
index bfa067c..0000000
--- a/na/kernel/sys_mm.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Memory Management System Calls
- *
- * Implements: brk, mmap (anonymous only), munmap, mprotect.
- *
- * Reference: docs/specs/syscall.md §7.2
- */
-
-#include <lume/types.h>
-#include <lume/task.h>
-#include <lume/vmm.h>
-#include <lume/errno.h>
-#include <lume/mman.h>
-#include <lume/config.h>
-#include <lume/klog.h>
-
-
-/* Page alignment helpers */
-static inline uint64 page_align_up(uint64 x)
-{
- return (x + kPageSize - 1) & ~(kPageSize - 1);
-}
-
-static inline uint64 page_align_down(uint64 x)
-{
- return x & ~(kPageSize - 1);
-}
-
-/* ======================================================================
- * sys_brk
- * ====================================================================== */
-
-int64 sys_brk(uint64 new_brk, uint64, uint64, uint64, uint64, uint64)
-{
- VmSpace* mm = current_task()->mm;
- if (!mm)
- return -ENOMEM; /* kernel threads have no user address space */
-
- if (new_brk == 0) {
- return static_cast<int64>(mm->brk_end);
- }
-
- if (mm->brk_start == 0) {
- uint64 init_brk = page_align_up(new_brk);
- mm->brk_start = init_brk;
- mm->brk_end = init_brk;
- }
-
- if (new_brk < mm->brk_start) {
- return -EINVAL;
- }
-
- uint64 new_end = page_align_up(new_brk);
- if (new_end > mm->brk_end) {
- uint64 grow = new_end - mm->brk_end;
- int ret = vmm_map_user(mm, mm->brk_end, grow, VM_READ | VM_WRITE);
- if (ret < 0) {
- return ret;
- }
- } else if (new_end < mm->brk_end) {
- uint64 shrink = mm->brk_end - new_end;
- int ret = vmm_unmap_user(mm, new_end, shrink);
- if (ret < 0) {
- return ret;
- }
- }
-
- mm->brk_end = new_end;
- return static_cast<int64>(new_brk);
-}
-
-/* ======================================================================
- * sys_mmap
- * ====================================================================== */
-
-int64 sys_mmap(uint64 addr, uint64 length, uint64 prot,
- uint64 flags, uint64 fd, uint64 offset)
-{
- printk("[mmap] addr=%llx len=%llx prot=%llx flags=%llx fd=%lld off=%llx\n",
- addr, length, prot, flags, static_cast<int64>(fd), offset);
-
- if (length == 0) return -EINVAL;
- length = page_align_up(length);
- if (offset % kPageSize != 0) return -EINVAL;
-
- if ((flags & MAP_PRIVATE) && (flags & MAP_SHARED)) return -EINVAL;
- if (!(flags & MAP_PRIVATE) && !(flags & MAP_SHARED)) return -EINVAL;
-
- VmSpace* mm = current_task()->mm;
- if (!mm) return -ENOMEM;
-
- /* Phase 2: only anonymous private mappings */
- if (!(flags & MAP_ANONYMOUS)) {
- printk("[mmap] REJECTED: file-backed mmap not implemented\n");
- return -ENOSYS; /* File-backed mmap is Phase 3 */
- }
- if (flags & MAP_SHARED)
- return -ENOSYS; /* Shared mappings are Phase 3 */
-
- (void)fd;
-
- /* Convert POSIX prot → MI permission flags */
- uint64 vm_perm = 0;
- if (prot & PROT_READ) vm_perm |= VM_READ;
- if (prot & PROT_WRITE) vm_perm |= VM_WRITE;
- if (prot & PROT_EXEC) vm_perm |= VM_EXEC;
- vm_perm |= VM_USER;
-
- uint64 va = addr;
- if (flags & MAP_FIXED) {
- if (va % kPageSize != 0) return -EINVAL;
- } else if (va != 0) {
- va = page_align_down(va);
- }
-
- if (va == 0) {
- va = mm_find_free_va(mm, length, kPageSize);
- if (!va) return -ENOMEM;
- }
-
- if (flags & MAP_FIXED) {
- int ret = vmm_unmap_user(mm, va, length);
- if (ret < 0) return ret;
- }
-
- int ret = vmm_map_user(mm, va, length, vm_perm);
- if (ret < 0 && !(flags & MAP_FIXED) && addr != 0) {
- uint64 fallback = mm_find_free_va(mm, length, kPageSize);
- if (!fallback) return ret;
- ret = vmm_map_user(mm, fallback, length, vm_perm);
- if (ret < 0) return ret;
- va = fallback;
- } else if (ret < 0) {
- return ret;
- }
-
- return static_cast<int64>(va);
-}
-
-/* ======================================================================
- * sys_munmap
- * ====================================================================== */
-
-int64 sys_munmap(uint64 addr, uint64 length, uint64, uint64, uint64, uint64)
-{
- if (length == 0) return -EINVAL;
- if (addr % kPageSize != 0) return -EINVAL;
- length = page_align_up(length);
-
- VmSpace* mm = current_task()->mm;
- if (!mm) return -EINVAL;
-
- return vmm_unmap_user(mm, addr, length);
-}
-
-/* ======================================================================
- * sys_mprotect — Phase 2 stub
- * ====================================================================== */
-
-int64 sys_mprotect(uint64 addr, uint64 len, uint64 prot,
- uint64, uint64, uint64)
-{
- if (len == 0) return -EINVAL;
- if (addr % kPageSize != 0) return -EINVAL;
-
- VmSpace* mm = current_task()->mm;
- if (!mm) return -ENOMEM;
-
- uint64 vm_perm = 0;
- if (prot & PROT_READ) vm_perm |= VM_READ;
- if (prot & PROT_WRITE) vm_perm |= VM_WRITE;
- if (prot & PROT_EXEC) vm_perm |= VM_EXEC;
-
- return vmm_protect_user(mm, addr, len, vm_perm);
-}
diff --git a/na/kernel/sys_process.cc b/na/kernel/sys_process.cc
deleted file mode 100644
index 438ad1b..0000000
--- a/na/kernel/sys_process.cc
+++ /dev/null
@@ -1,185 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Process Control System Calls
- *
- * Implements: exit, exit_group, getpid, gettid, getppid,
- * sched_yield, clone (stub), wait4 (stub),
- * set_tid_address, getuid, getgid.
- *
- * Reference: docs/specs/syscall.md §7.1
- */
-
-#include "lume/klog.h"
-#include <lume/types.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-#include <lume/errno.h>
-#include <lume/uaccess.h>
-#include <lume/wait.h>
-
-/* External: task lifecycle (kernel/task.cc) */
-extern void task_exit(int code);
-extern int32 do_wait4(int32 wait_pid, int options, int32* exit_code);
-extern int32 task_clone(arch::TrapFrame* tf, uint64 clone_flags,
- uint64 child_stack, uint64 parent_tidptr,
- uint64 child_tidptr, uint64 tls);
-extern int32 task_fork(arch::TrapFrame* tf);
-
-/* ======================================================================
- * sys_exit / sys_exit_group
- * ====================================================================== */
-
-int64 sys_exit(uint64 code, uint64, uint64, uint64, uint64, uint64)
-{
- task_exit(static_cast<int>(code & 0xFF));
- __builtin_unreachable();
-}
-
-int64 sys_exit_group(uint64 code, uint64, uint64, uint64, uint64, uint64)
-{
- /* Phase 2: single-threaded, equivalent to sys_exit.
- * Phase 3: kill all threads in tgid then exit. */
- task_exit(static_cast<int>(code & 0xFF));
- __builtin_unreachable();
-}
-
-/* ======================================================================
- * sys_getpid / sys_gettid / sys_getppid
- * ====================================================================== */
-
-int64 sys_getpid(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- return current_task()->tgid; /* Linux: getpid() returns tgid */
-}
-
-int64 sys_gettid(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- return current_task()->pid; /* Linux: gettid() returns real tid */
-}
-
-int64 sys_getppid(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- TaskControlBlock* parent = current_task()->parent;
- return parent ? parent->tgid : 0;
-}
-
-/* ======================================================================
- * sys_sched_yield
- * ====================================================================== */
-
-int64 sys_sched_yield_wrapper(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- sched_yield();
- return 0;
-}
-
-int64 sys_clone_wrapper(uint64 flags, uint64 child_stack,
- uint64 ptid, uint64 ctid, uint64 tls, uint64)
-{
- arch::TrapFrame* tf = current_task()->trapframe;
- if (!tf) return -EINVAL;
-
- return task_clone(tf, flags, child_stack, ptid, ctid, tls);
-}
-
-/* ========================================================================
- * sys_wait4 — Wait for child to exit
- * ======================================================================== */
-
-int64 sys_wait4_wrapper(uint64 pid, uint64 wstatus_ptr,
- uint64 options, uint64, uint64, uint64)
-{
- int32 exit_code = 0;
- int32 child_pid = do_wait4(static_cast<int32>(pid),
- static_cast<int>(options),
- &exit_code);
-
- if (child_pid < 0) return child_pid;
-
- /* exit_code is already in Linux wstatus format */
- if (wstatus_ptr != 0) {
- if (copy_to_user(wstatus_ptr, &exit_code, sizeof(int)) < 0)
- return -EFAULT;
- }
-
- return child_pid;
-}
-
-/* ======================================================================
- * sys_set_tid_address — musl libc calls this very early
- * ====================================================================== */
-
-int64 sys_set_tid_address(uint64 tidptr, uint64, uint64,
- uint64, uint64, uint64)
-{
- /* Store clear_child_tid pointer — on exit, kernel writes 0 here
- * and does futex_wake (enables pthread_join). */
- current_task()->clear_child_tid = tidptr;
- return current_task()->pid;
-}
-
-/* ======================================================================
- * sys_getuid / sys_getgid — Phase 2: always root
- * ====================================================================== */
-
-int64 sys_getuid(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- return 0; /* root */
-}
-
-int64 sys_getgid(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- return 0; /* root */
-}
-
-/* ======================================================================
- * sys_nanosleep (SYS_nanosleep = 101)
- * ====================================================================== */
-#include <lume/wait.h>
-#include <lume/kprintf.h>
-#include <lume/timer.h>
-
-struct linux_timespec {
- int64 tv_sec;
- int64 tv_nsec;
-};
-
-static void nanosleep_wakeup(TimerList* t) {
- TaskControlBlock* task = static_cast<TaskControlBlock*>(t->data);
- task->state = TaskState::Runnable;
- sched_enqueue(task);
-}
-
-int64 sys_nanosleep(uint64 req_ptr, uint64 rem_ptr, uint64, uint64, uint64, uint64) {
- if (req_ptr == 0) return -EINVAL;
-
- linux_timespec req;
- if (copy_from_user(&req, req_ptr, sizeof(req)) != 0) {
- return -EFAULT;
- }
-
- uint64 target = *(volatile uint64*)&g_jiffies + (req.tv_sec * 100) + (req.tv_nsec / 10000000);
-
- TaskControlBlock* curr = current_task();
- curr->state = TaskState::Sleeping;
-
- TimerList t;
- t.init(nanosleep_wakeup);
- t.expires = target;
- t.data = curr;
-
- add_timer(&t);
-
- if (*(volatile uint64*)&g_jiffies < target) {
- schedule();
- } else {
- curr->state = TaskState::Running;
- }
-
-
- del_timer(&t);
-
- return 0;
-}
diff --git a/na/kernel/sys_signal.cc b/na/kernel/sys_signal.cc
deleted file mode 100644
index 8b459d5..0000000
--- a/na/kernel/sys_signal.cc
+++ /dev/null
@@ -1,351 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * sys_signal.cc — Signal-related system calls
- *
- * Implements:
- * sys_kill (SYS_kill = 129)
- * sys_rt_sigaction (SYS_rt_sigaction = 134)
- * sys_rt_sigprocmask (SYS_rt_sigprocmask = 135)
- * sys_rt_sigreturn (SYS_rt_sigreturn = 139)
- *
- * Reference: Linux kernel/signal.c, arch/riscv/kernel/signal.c
- */
-
-#include <lume/signal.h>
-#include <lume/signal_types.h>
-#include <lume/signal_nr.h>
-#include <lume/task.h>
-#include <lume/errno.h>
-#include <lume/uaccess.h>
-#include <lume/kprintf.h>
-#include <lume/klog.h>
-#include <lume/new.h>
-#include <lume/pgrp.h>
-
-/* From signal.cc */
-extern int send_signal(int sig, kernel_siginfo* info, TaskControlBlock* task);
-
-/* From task.cc */
-extern list_node g_all_tasks;
-extern Spinlock g_task_list_lock;
-
-/* ================================================================== */
-/* Helper: find task by PID */
-/* ================================================================== */
-
-static TaskControlBlock* find_task_by_pid(int32 pid)
-{
- LockGuard guard(g_task_list_lock);
- list_node* node = g_all_tasks.next;
- while (node != &g_all_tasks) {
- auto* task = list_entry<TaskControlBlock, &TaskControlBlock::all_link>(node);
- if (task->pid == pid) return task;
- node = node->next;
- }
- return nullptr;
-}
-
-/* ================================================================== */
-/* sys_kill — send a signal to a process */
-/* ================================================================== */
-
-/*
- * Deliver `sig` to the thread-group leader represented by `sig_st`.
- * sighand is allocated eagerly at fork/exec time, so we just trust
- * that the leader has one; send_signal returns -ESRCH if not.
- */
-static int deliver_to_group(signal_struct* sig_st, int sig,
- kernel_siginfo* info)
-{
- if (!sig_st || !sig_st->leader) return 0;
- if (send_signal(sig, info, sig_st->leader) == 0) return 1;
- return 0;
-}
-
-int64 sys_kill(uint64 a0, uint64 a1, uint64, uint64, uint64, uint64)
-{
- int32 pid = static_cast<int32>(a0);
- int sig = static_cast<int>(a1);
-
- if (sig < 0 || sig > _NSIG) return -EINVAL;
-
- TaskControlBlock* curr = current_task();
- if (!curr) return -ESRCH;
-
- kernel_siginfo info{};
- info.si_signo = sig;
- info.si_code = 0; /* SI_USER */
- info._sifields._kill._pid = curr->pid;
- info._sifields._kill._uid = 0;
-
- /* pid > 0: deliver to that thread group's leader. */
- if (pid > 0) {
- TaskControlBlock* target = find_task_by_pid(pid);
- if (!target) return -ESRCH;
- /* Group signal: prefer the thread-group leader if available. */
- if (target->signal && target->signal->leader)
- target = target->signal->leader;
- if (sig == 0) return 0;
- return send_signal(sig, &info, target);
- }
-
- /* pid == 0: every member of caller's process group. */
- if (pid == 0) {
- if (!curr->signal) return -ESRCH;
- if (sig == 0) return 0;
- int delivered = 0;
- int32 target_pgid = curr->signal->pgid;
- for_each_pgrp_member(target_pgid, [&](signal_struct* s) {
- delivered += deliver_to_group(s, sig, &info);
- });
- return delivered > 0 ? 0 : -ESRCH;
- }
-
- /* pid == -1: every process the caller may signal, except init.
- * We approximate "every" with "every thread-group leader on every
- * pgrp bucket". This is rare in practice (BusyBox doesn't use it
- * during normal shell operation). */
- if (pid == -1) {
- if (sig == 0) return 0;
- int delivered = 0;
- LockGuard guard(g_pgrp_lock);
- for (auto& bucket : g_pgrp_hash) {
- list_node* node = bucket.next;
- while (node != &bucket) {
- list_node* next = node->next;
- auto* s = list_entry<signal_struct,
- &signal_struct::pgrp_link>(node);
- if (s->leader && s->leader->pid != 1)
- delivered += deliver_to_group(s, sig, &info);
- node = next;
- }
- }
- return delivered > 0 ? 0 : -ESRCH;
- }
-
- /* pid < -1: every member of pgrp |pid|. */
- int32 target_pgid = -pid;
- if (sig == 0) {
- bool any = false;
- for_each_pgrp_member(target_pgid, [&](signal_struct*) { any = true; });
- return any ? 0 : -ESRCH;
- }
- int delivered = 0;
- for_each_pgrp_member(target_pgid, [&](signal_struct* s) {
- delivered += deliver_to_group(s, sig, &info);
- });
- return delivered > 0 ? 0 : -ESRCH;
-}
-
-/* ================================================================== */
-/* sys_tkill / sys_tgkill — send a signal to a specific thread */
-/* ================================================================== */
-
-static int64 do_tkill(int32 tgid, int32 tid, int sig)
-{
- if (sig < 0 || sig > _NSIG) return -EINVAL;
- if (tid <= 0) return -EINVAL;
- TaskControlBlock* target = find_task_by_pid(tid);
- if (!target) return -ESRCH;
- /* tgid == -1 from tkill means "any tgid"; otherwise enforce match. */
- if (tgid != -1 && target->tgid != tgid) return -ESRCH;
-
- if (sig == 0) return 0;
-
- kernel_siginfo info{};
- info.si_signo = sig;
- info.si_code = 0; /* SI_TKILL would be -6; userland rarely checks */
- info._sifields._kill._pid = current_task() ? current_task()->pid : 0;
- info._sifields._kill._uid = 0;
- return send_signal(sig, &info, target);
-}
-
-int64 sys_tkill(uint64 a0, uint64 a1, uint64, uint64, uint64, uint64)
-{
- return do_tkill(-1,
- static_cast<int32>(a0),
- static_cast<int>(a1));
-}
-
-int64 sys_tgkill(uint64 a0, uint64 a1, uint64 a2, uint64, uint64, uint64)
-{
- return do_tkill(static_cast<int32>(a0),
- static_cast<int32>(a1),
- static_cast<int>(a2));
-}
-
-/* ================================================================== */
-/* sys_rt_sigaction — get/set signal disposition */
-/* ================================================================== */
-
-/*
- * User-space sigaction struct layout (RISC-V LP64):
- * uint64 sa_handler;
- * uint64 sa_flags;
- * uint64 sa_restorer; (SA_RESTORER)
- * sigset_t sa_mask; (8 bytes on LP64)
- */
-struct user_sigaction {
- uint64 sa_handler;
- uint64 sa_flags;
- uint64 sa_restorer;
- sigset_t sa_mask;
-};
-
-int64 sys_rt_sigaction(uint64 a0, uint64 a1, uint64 a2, uint64 a3,
- uint64, uint64)
-{
- int sig = static_cast<int>(a0);
- uint64 act_uaddr = a1; /* const struct sigaction __user * */
- uint64 oact_uaddr = a2; /* struct sigaction __user * */
- uint64 sigsetsize = a3;
-
- if (sig < 1 || sig > _NSIG) return -EINVAL;
- if (sig_kernel_only(sig)) return -EINVAL;
- if (sigsetsize != sizeof(sigset_t)) return -EINVAL;
-
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->sighand) return -EINVAL;
-
- /* All reads/writes of the action table happen under siglock —
- * Linux's do_sigaction() also takes the same lock (kernel/signal.c). */
- user_sigaction old{};
- user_sigaction act{};
- bool have_new = (act_uaddr != 0);
-
- if (have_new) {
- int err = copy_from_user(&act, act_uaddr, sizeof(act));
- if (err < 0) return -EFAULT;
- }
-
- {
- LockGuard guard(curr->sighand->siglock);
- kernel_sigaction* ksa = &curr->sighand->action[sig - 1];
- old.sa_handler = ksa->sa_handler;
- old.sa_flags = ksa->sa_flags;
- old.sa_restorer = ksa->sa_restorer;
- old.sa_mask = ksa->sa_mask;
-
- if (have_new) {
- ksa->sa_handler = act.sa_handler;
- ksa->sa_flags = act.sa_flags;
- ksa->sa_restorer = act.sa_restorer;
- ksa->sa_mask = act.sa_mask;
- ksa->sa_mask.del(SIGKILL);
- ksa->sa_mask.del(SIGSTOP);
- }
- }
-
- if (oact_uaddr != 0) {
- int err = copy_to_user(oact_uaddr, &old, sizeof(old));
- if (err < 0) return -EFAULT;
- }
- return 0;
-}
-
-/* ================================================================== */
-/* sys_rt_sigprocmask — change signal blocked mask */
-/* ================================================================== */
-
-int64 sys_rt_sigprocmask(uint64 a0, uint64 a1, uint64 a2, uint64 a3,
- uint64, uint64)
-{
- int how = static_cast<int>(a0);
- uint64 set_uaddr = a1;
- uint64 oset_uaddr = a2;
- uint64 sigsetsize = a3;
-
- if (sigsetsize != sizeof(sigset_t)) return -EINVAL;
-
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->sighand) return -EINVAL;
-
- sigset_t newset{};
- bool have_new = (set_uaddr != 0);
- if (have_new) {
- int err = copy_from_user(&newset, set_uaddr, sizeof(sigset_t));
- if (err < 0) return -EFAULT;
- newset.del(SIGKILL);
- newset.del(SIGSTOP);
- }
-
- sigset_t old{};
- {
- /* Linux: kernel/signal.c:do_sigprocmask() also holds siglock
- * across read+update so the snapshot is atomic vs. concurrent
- * signal delivery. */
- LockGuard guard(curr->sighand->siglock);
- old = curr->blocked;
- if (have_new) {
- switch (how) {
- case SIG_BLOCK:
- curr->blocked = curr->blocked | newset;
- break;
- case SIG_UNBLOCK:
- curr->blocked = curr->blocked & ~newset;
- break;
- case SIG_SETMASK:
- curr->blocked = newset;
- break;
- default:
- return -EINVAL;
- }
- }
- }
-
- if (oset_uaddr != 0) {
- int err = copy_to_user(oset_uaddr, &old, sizeof(sigset_t));
- if (err < 0) return -EFAULT;
- }
- return 0;
-}
-
-/* ================================================================== */
-/* sys_rt_sigreturn — restore context after signal handler */
-/* ================================================================== */
-
-/* Arch-specific: implemented in arch/riscv/signal.cc */
-extern int64 arch_sigreturn(arch::TrapFrame* tf);
-
-int64 sys_rt_sigreturn(uint64, uint64, uint64, uint64, uint64, uint64)
-{
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->trapframe) return -EINVAL;
-
- return arch_sigreturn(curr->trapframe);
-}
-
-/* ================================================================== */
-/* sys_rt_sigtimedwait (SYS_rt_sigtimedwait = 137) */
-/* ================================================================== */
-#include <lume/timer.h>
-#include <lume/sched.h>
-
-struct linux_timespec {
- int64 tv_sec;
- int64 tv_nsec;
-};
-
-int64 sys_rt_sigtimedwait(uint64 /* set_ptr */, uint64 /* info_ptr */, uint64 timeout_ptr, uint64 /* sigsetsize */, uint64, uint64)
-{
- /*
- * COMPATIBILITY STUB:
- * LumeOS lacks full real-time signal queues and timed waiting mechanism.
- * When BusyBox init runs out of children to reap, it calls this to wait
- * for incoming signals (like SIGCHLD) with a timeout.
- * We simulate the timeout by yielding the CPU, then return -EAGAIN (Try again)
- * which tells user space that the timeout expired without receiving a signal.
- */
- if (timeout_ptr != 0) {
- linux_timespec req;
- if (copy_from_user(&req, timeout_ptr, sizeof(req)) == 0) {
- uint64 target = g_jiffies + (req.tv_sec * 100) + (req.tv_nsec / 10000000);
- while (g_jiffies < target) {
- schedule();
- }
- }
- }
- return -EAGAIN;
-}
diff --git a/na/kernel/syscall.cc b/na/kernel/syscall.cc
deleted file mode 100644
index a5b80ec..0000000
--- a/na/kernel/syscall.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * System Call MI Layer — Jump Table & Dispatcher
- *
- * O(1) function pointer array dispatch. No switch-case.
- * syscall_init() registers all implemented handlers.
- *
- * Reference: docs/specs/syscall.md §6
- */
-
-#include <lume/syscall.h>
-#include <lume/syscall_nr.h>
-#include <lume/errno.h>
-#include <lume/kprintf.h>
-#include <lume/klog.h>
-#include <lume/console.h>
-#include <arch/cpu.h>
-
-/* ======================================================================
- * Jump table
- * ====================================================================== */
-
-inline constexpr int kNrSyscalls = 512;
-
-static SyscallHandler sys_call_table[kNrSyscalls];
-
-/* ======================================================================
- * MI Dispatcher
- * ====================================================================== */
-
-int64 syscall_dispatch(uint64 nr, uint64 args[6])
-{
- if (nr >= static_cast<uint64>(kNrSyscalls)) {
- kprintf("[syscall] nr=%llu out of range\n", nr);
- return -ENOSYS;
- }
-
- /* Per-syscall entry trace. KLOG_DEBUG keeps it out of the console
- * by default; enable via klog_set_level(KLOG_DEBUG) or read the
- * ring buffer to investigate. Subsys mask gates this further. */
- pr_debug_s(KLOG_SUBSYS_SYSCALL,
- "syscall: nr=%llu a0=%llx a1=%llx a2=%llx a3=%llx\n",
- nr, args[0], args[1], args[2], args[3]);
-
- SyscallHandler handler = sys_call_table[nr];
- if (!handler) {
- kprintf("[syscall] unimplemented nr=%llu\n", nr);
- return -ENOSYS;
- }
-
- return handler(args[0], args[1], args[2],
- args[3], args[4], args[5]);
-}
-
-/* ======================================================================
- * Forward declarations of sys_* handlers
- * ====================================================================== */
-
-/* sys_process.cc */
-int64 sys_exit(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_exit_group(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getpid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_gettid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getppid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_sched_yield_wrapper(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_clone_wrapper(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_wait4_wrapper(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_set_tid_address(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getuid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getgid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_nanosleep(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* sys_mm.cc */
-int64 sys_brk(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_mmap(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_munmap(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_mprotect(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* sys_info.cc */
-int64 sys_uname(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* sys_fs.cc */
-int64 sys_openat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_close(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_read(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_write(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_lseek(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_fstat_wrapper(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getdents64(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_dup(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_dup3(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getcwd(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_chdir(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_mkdirat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_unlinkat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_writev(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_ioctl(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_ppoll(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_pipe2(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_fcntl(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_socket(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_sendto(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* sys_signal.cc */
-int64 sys_kill(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_tkill(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_tgkill(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_rt_sigaction(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_rt_sigprocmask(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_rt_sigreturn(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_rt_sigtimedwait(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* exec.cc */
-int64 sys_execve_wrapper(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* sys_misc.cc */
-int64 sys_reboot(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_prctl(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_setsid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getsid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_setpgid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getpgid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_geteuid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getegid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_sigaltstack(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_clock_gettime(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* ======================================================================
- * syscall_init — register all handlers
- * ====================================================================== */
-
-void syscall_init()
-{
- /* Zero the table (redundant for BSS, but explicit) */
- for (int i = 0; i < kNrSyscalls; i++)
- sys_call_table[i] = nullptr;
-
- /* Phase 2: minimal runnable set */
- sys_call_table[SYS_exit] = sys_exit;
- sys_call_table[SYS_exit_group] = sys_exit_group;
- sys_call_table[SYS_getpid] = sys_getpid;
- sys_call_table[SYS_gettid] = sys_gettid;
- sys_call_table[SYS_getppid] = sys_getppid;
- sys_call_table[SYS_sched_yield] = sys_sched_yield_wrapper;
- sys_call_table[SYS_clone] = sys_clone_wrapper;
- sys_call_table[SYS_wait4] = sys_wait4_wrapper;
- sys_call_table[SYS_set_tid_address] = sys_set_tid_address;
- sys_call_table[SYS_getuid] = sys_getuid;
- sys_call_table[SYS_getgid] = sys_getgid;
- sys_call_table[SYS_brk] = sys_brk;
- sys_call_table[SYS_mmap] = sys_mmap;
- sys_call_table[SYS_munmap] = sys_munmap;
- sys_call_table[SYS_mprotect] = sys_mprotect;
- sys_call_table[SYS_uname] = sys_uname;
-
- /* Phase 3: filesystem */
- sys_call_table[SYS_openat] = sys_openat;
- sys_call_table[SYS_close] = sys_close;
- sys_call_table[SYS_read] = sys_read;
- sys_call_table[SYS_write] = sys_write;
- sys_call_table[SYS_lseek] = sys_lseek;
- sys_call_table[SYS_fstat] = sys_fstat_wrapper;
- sys_call_table[SYS_getdents64] = sys_getdents64;
- sys_call_table[SYS_dup] = sys_dup;
- sys_call_table[SYS_dup3] = sys_dup3;
- sys_call_table[SYS_getcwd] = sys_getcwd;
- sys_call_table[SYS_chdir] = sys_chdir;
- sys_call_table[SYS_mkdirat] = sys_mkdirat;
- sys_call_table[SYS_unlinkat] = sys_unlinkat;
- sys_call_table[SYS_writev] = sys_writev;
- sys_call_table[SYS_ioctl] = sys_ioctl;
- sys_call_table[SYS_ppoll] = sys_ppoll;
-
- /* Phase 4: signals */
- sys_call_table[SYS_kill] = sys_kill;
- sys_call_table[SYS_tkill] = sys_tkill;
- sys_call_table[SYS_tgkill] = sys_tgkill;
- sys_call_table[SYS_rt_sigaction] = sys_rt_sigaction;
- sys_call_table[SYS_rt_sigprocmask] = sys_rt_sigprocmask;
- sys_call_table[SYS_rt_sigreturn] = sys_rt_sigreturn;
- sys_call_table[SYS_rt_sigtimedwait] = sys_rt_sigtimedwait;
-
- /* Phase 5: exec */
- sys_call_table[SYS_execve] = sys_execve_wrapper;
-
- /* Phase 6: pipe + fcntl */
- sys_call_table[SYS_pipe2] = sys_pipe2;
- sys_call_table[SYS_fcntl] = sys_fcntl;
-
- /* Phase 7: busybox bootstrap */
- sys_call_table[SYS_reboot] = sys_reboot;
- sys_call_table[SYS_prctl] = sys_prctl;
- sys_call_table[SYS_setsid] = sys_setsid;
- sys_call_table[SYS_getsid] = sys_getsid;
- sys_call_table[SYS_setpgid] = sys_setpgid;
- sys_call_table[SYS_getpgid] = sys_getpgid;
- sys_call_table[SYS_geteuid] = sys_geteuid;
- sys_call_table[SYS_getegid] = sys_getegid;
- sys_call_table[SYS_sigaltstack] = sys_sigaltstack;
- sys_call_table[SYS_clock_gettime] = sys_clock_gettime;
- sys_call_table[SYS_nanosleep] = sys_nanosleep;
- sys_call_table[SYS_socket] = sys_socket;
- sys_call_table[SYS_sendto] = sys_sendto;
-
- printk(KERN_INFO "init: syscall_init done (%d handlers registered)\n", 46);
-}
diff --git a/na/kernel/task.cc b/na/kernel/task.cc
deleted file mode 100644
index 9f9b043..0000000
--- a/na/kernel/task.cc
+++ /dev/null
@@ -1,780 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Task Management — create, exit, destroy, kthread
- *
- * Implements task lifecycle management: kernel thread creation,
- * task exit (zombie transition), and resource reclamation.
- *
- * Reference: docs/specs/task.md §4-§6
- */
-
-#include <lume/task.h>
-#include <lume/sched.h>
-#include <lume/pmm.h>
-#include <lume/addr.h>
-#include <lume/new.h>
-#include <lume/kprintf.h>
-#include <lume/klog.h>
-#include <lume/vmm.h>
-#include <lume/errno.h>
-#include <lume/pid.h>
-#include <lume/wait.h>
-#include <lume/clone.h>
-#include <lume/signal_nr.h>
-#include <arch/cpu.h>
-#include <arch/context.h>
-#include <lume/uaccess.h>
-#include <lume/fs_struct.h>
-#include <lume/fdtable.h>
-#include <lume/signal.h>
-#include <lume/signal_types.h>
-#include <lume/pgrp.h>
-#include <arch/pmap.h>
-#include <kernel/sync/spinlock.h>
-
-/* ======================================================================
- * Forward declarations
- * ====================================================================== */
-
-extern "C" void kthread_entry_trampoline();
-extern "C" void ret_from_fork();
-
-/* ======================================================================
- * Global task list
- * ====================================================================== */
-
-list_node g_all_tasks;
-Spinlock g_task_list_lock;
-int32 g_nr_tasks = 0;
-
-/* Special tasks */
-TaskControlBlock* g_kernel_task = nullptr; /* PID 0 container */
-TaskControlBlock* g_init_task = nullptr; /* PID 1 (future) */
-
-/* ======================================================================
- * task_init() — called once from kernel_main before sched_init
- * ====================================================================== */
-
-void task_init()
-{
- g_all_tasks.init();
- g_task_list_lock.init("task_list");
- g_pid_alloc.init();
-
- printk(KERN_INFO "init: task_init done (pid_alloc ready)\n");
-}
-
-/* ======================================================================
- * alloc_task — allocate TCB + kernel stack
- *
- * This is the SINGLE canonical TCB initializer. Both kthread_create
- * (task.cc) and sched_init (sched_core.cc) must use this function.
- * NEVER duplicate this logic — adding fields to TaskControlBlock
- * requires updating only this function.
- * ====================================================================== */
-
-TaskControlBlock* alloc_task(const char* name, uint8 priority,
- uint8 flags, uint8 cpu)
-{
- auto* task = new TaskControlBlock;
- if (!task) return nullptr;
-
- task->context = {};
-
- /* Allocate 8KB kernel stack (order-1 = 2 pages) */
- Page* stack_page = pmm_alloc_pages(1);
- if (!stack_page) {
- delete task;
- return nullptr;
- }
- task->kstack_pa = page_to_pa(stack_page).raw;
- task->kstack_va = pa_to_va(phys_addr(task->kstack_pa)).raw;
-
- /* Write stack canary at bottom */
- *reinterpret_cast<uint64*>(task->kstack_va) = kStackCanary;
-
- /* Place TrapFrame at the top of the kernel stack (like Linux pt_regs).
- * ALL tasks — kernel threads and user processes — have a TrapFrame
- * here. kthread_create sets the initial SP below the TrapFrame so
- * the kthread function never touches this reserved area.
- * kernel_execve / do_execve can later fill it in for user-mode return. */
- uint64 tf_addr = task->kstack_va + kKernelStackSize
- - sizeof(arch::TrapFrame);
- task->trapframe = reinterpret_cast<arch::TrapFrame*>(tf_addr);
- __builtin_memset(task->trapframe, 0, sizeof(arch::TrapFrame));
-
- /* Basic fields */
- task->name = name;
- task->priority = priority;
- task->time_slice = priority_to_timeslice(priority);
- task->state = TaskState::Runnable;
- task->cpu_id = cpu;
- task->flags = flags;
- task->exit_code = 0;
- task->exit_signal = 0;
- task->preempt_count = 0;
- task->clear_child_tid = 0;
- task->stopped_code = 0;
- task->notify_continued = false;
- task->parent = nullptr;
- task->mm = nullptr;
- task->fpstate = {};
- task->files = nullptr;
- task->fs = nullptr;
- task->pending.init();
- task->blocked.clear();
- task->sighand = nullptr;
- task->signal = nullptr;
-
- /* Init all list nodes */
- task->run_link.init();
- task->all_link.init();
- task->children.init();
- task->sibling.init();
- task->lock.init("task");
- task->wait_chld.init("wait_chld");
-
- /* Default sched class */
- extern const SchedClass g_sched_class_o1;
- task->sched_class = &g_sched_class_o1;
- task->sched_entity = &task->run_link;
-
- return task;
-}
-
-/* Add task to global task list */
-static void task_list_add(TaskControlBlock* task)
-{
- LockGuard guard(g_task_list_lock);
-
- list_node* head = &g_all_tasks;
- task->all_link.prev = head->prev;
- task->all_link.next = head;
- head->prev->next = &task->all_link;
- head->prev = &task->all_link;
-
- g_nr_tasks++;
-}
-
-/* Remove task from global task list */
-static void task_list_remove(TaskControlBlock* task)
-{
- LockGuard guard(g_task_list_lock);
-
- task->all_link.prev->next = task->all_link.next;
- task->all_link.next->prev = task->all_link.prev;
- task->all_link.init();
-
- if (g_nr_tasks > 0)
- g_nr_tasks--;
-}
-
-/* ======================================================================
- * kthread_create — create a kernel thread
- * ====================================================================== */
-
-TaskControlBlock* kthread_create(const char* name,
- void (*entry)(void*),
- void* arg,
- uint8 priority)
-{
- TaskControlBlock* task = alloc_task(name, priority, TF_KTHREAD,
- static_cast<uint8>(arch::cpu::id()));
- if (!task) return nullptr;
-
- /* Allocate PID */
- task->pid = g_pid_alloc.alloc();
- if (task->pid < 0) {
- /* PID exhausted — clean up */
- pmm_free_pages(pa_to_page(phys_addr(task->kstack_pa)), 1);
- delete task;
- return nullptr;
- }
- task->tgid = task->pid;
-
- /* Kernel threads: exit_signal = -1 → auto-reap on exit.
- * Linux: kernel_thread() passes (flags & ~CSIGNAL) → exit_signal = 0,
- * but kthreads' parent (kthreadd) reaps them. We have no kthreadd,
- * so we use -1 which makes exit_notify() call release_task() directly,
- * identical to Linux's CLONE_THREAD autoreap path. */
- task->exit_signal = -1;
-
- /* Set up context for kthread_entry_trampoline.
- * SP starts BELOW the TrapFrame reserved at the stack top.
- * This ensures the kthread function never clobbers the TrapFrame area. */
- uint64 stack_top = task->kstack_va + kKernelStackSize
- - sizeof(arch::TrapFrame);
- task->context = {};
- task->context.ra = reinterpret_cast<uint64>(kthread_entry_trampoline);
- task->context.sp = stack_top;
- task->context.s[0] = reinterpret_cast<uint64>(entry);
- task->context.s[1] = reinterpret_cast<uint64>(arg);
-
- /* Parent is kernel_task (PID 0) or current caller */
- TaskControlBlock* parent = g_kernel_task ? g_kernel_task : current_task();
- task->parent = parent;
- if (parent) {
- LockGuard guard(parent->lock);
- list_node* head = &parent->children;
- task->sibling.prev = head->prev;
- task->sibling.next = head;
- head->prev->next = &task->sibling;
- head->prev = &task->sibling;
- }
-
- /* Add to global list and enqueue */
- task_list_add(task);
- sched_enqueue(task);
-
- return task;
-}
-
-/* ======================================================================
- * task_exit — transition current task to Zombie
- * ====================================================================== */
-
-void task_exit(int code)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) kernel_panic("task_exit: no current task");
-
- /* Linux ABI: normal exit encodes status as (code & 0xFF) << 8 */
- curr->exit_code = (code & 0xFF) << 8;
-
- /* CLONE_CHILD_CLEARTID / set_tid_address: write 0 to user tidptr.
- * This is how pthread_join detects thread termination.
- * TODO: futex_wake(clear_child_tid, 1) after writing 0. */
- if (curr->clear_child_tid != 0 && curr->mm != nullptr) {
- int32 zero = 0;
- copy_to_user(curr->clear_child_tid, &zero, sizeof(zero));
- curr->clear_child_tid = 0;
- }
-
- /* Release user address space (refcount-safe).
- * MUST switch to kernel page table FIRST — otherwise we're running
- * on a freed page table after put() deallocates it. */
- if (curr->mm != nullptr) {
- extern uint64 g_kernel_pgtbl;
- pmap::activate(g_kernel_pgtbl);
- curr->mm->put();
- curr->mm = nullptr;
- }
-
- /* Reparent children to init (or kernel_task if no init yet) */
- TaskControlBlock* foster = g_init_task ? g_init_task : g_kernel_task;
- if (foster && !curr->children.is_empty()) {
- LockGuard guard(curr->lock);
-
- list_node* child_node = curr->children.next;
- while (child_node != &curr->children) {
- list_node* next_sib = child_node->next;
- auto* child = list_entry<TaskControlBlock,
- &TaskControlBlock::sibling>(child_node);
- child->parent = foster;
-
- /* Move sibling link to foster's children */
- child_node->prev->next = child_node->next;
- child_node->next->prev = child_node->prev;
-
- list_node* fhead = &foster->children;
- child_node->prev = fhead->prev;
- child_node->next = fhead;
- fhead->prev->next = child_node;
- fhead->prev = child_node;
-
- child_node = next_sib;
- }
- curr->children.init();
- }
-
-
- /* ---- exit_notify: Linux kernel/exit.c:exit_notify() ----
- * Determine whether this task should be auto-reaped or become zombie.
- *
- * Linux logic (simplified):
- * - exit_signal == -1 (kthreads, sub-threads): autoreap = true
- * - exit_signal >= 1 (user processes): notify parent, become zombie
- *
- * If autoreap, we call release_task() equivalent inline:
- * free PID, remove from task list, then deschedule to Dead. */
- bool autoreap = (curr->exit_signal <= 0);
-
- if (autoreap) {
- /* Auto-reap: release resources immediately (no zombie state).
- * Linux: exit_notify() → tsk->exit_state = EXIT_DEAD → release_task() */
- task_list_remove(curr);
- g_pid_alloc.free(curr->pid);
-
- /* Remove from parent's children list */
- curr->sibling.prev->next = curr->sibling.next;
- curr->sibling.next->prev = curr->sibling.prev;
- curr->sibling.init();
-
- /* Drop signal_struct ref: last task in the thread group also
- * detaches us from the pgrp hash. */
- if (curr->signal) {
- put_signal_struct(curr->signal);
- curr->signal = nullptr;
- }
- if (curr->sighand) {
- put_sighand(curr->sighand);
- curr->sighand = nullptr;
- }
-
- curr->state = TaskState::Dead;
- schedule();
- __builtin_unreachable();
- }
-
- /* User process path: become Zombie for parent wait() */
- curr->state = TaskState::Zombie;
-
- /* Wake parent->wait_chld queue */
- if (curr->parent) {
- curr->parent->wait_chld.wake_all();
- }
-
- /* Yield CPU — never returns */
- schedule();
-
- __builtin_unreachable();
-}
-
-void task_kill_current(int sig)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) kernel_panic("task_kill_current: no current task");
-
- /* Linux ABI: signal-killed encodes status as (signo & 0x7F) */
- curr->exit_code = sig & 0x7F;
-
- /* Release user address space (refcount-safe).
- * Switch to kernel page table first (same as task_exit). */
- if (curr->mm) {
- extern uint64 g_kernel_pgtbl;
- pmap::activate(g_kernel_pgtbl);
- curr->mm->put();
- curr->mm = nullptr;
- }
-
- /* Reparent children (same as task_exit) */
- TaskControlBlock* foster = g_init_task ? g_init_task : g_kernel_task;
- if (foster && !curr->children.is_empty()) {
- LockGuard guard(curr->lock);
-
- list_node* child_node = curr->children.next;
- while (child_node != &curr->children) {
- list_node* next_sib = child_node->next;
- auto* child = list_entry<TaskControlBlock,
- &TaskControlBlock::sibling>(child_node);
- child->parent = foster;
-
- child_node->prev->next = child_node->next;
- child_node->next->prev = child_node->prev;
-
- list_node* fhead = &foster->children;
- child_node->prev = fhead->prev;
- child_node->next = fhead;
- fhead->prev->next = child_node;
- fhead->prev = child_node;
-
- child_node = next_sib;
- }
- curr->children.init();
- }
-
- curr->state = TaskState::Zombie;
-
- if (curr->parent) {
- curr->parent->wait_chld.wake_all();
- }
-
- schedule();
- __builtin_unreachable();
-}
-
-/* ======================================================================
- * task_destroy — fully reclaim a Zombie task (called by waitpid)
- * ====================================================================== */
-
-void task_destroy(TaskControlBlock* task)
-{
- /* Remove from global list */
- task_list_remove(task);
-
- /* Remove from parent's children */
- task->sibling.prev->next = task->sibling.next;
- task->sibling.next->prev = task->sibling.prev;
- task->sibling.init();
-
- /* Free PID */
- g_pid_alloc.free(task->pid);
-
- /* Drop signal_struct + sighand refs. For zombies these are delayed
- * until reap (here) so a waitpid() walker can still inspect
- * pgid/sid; Linux defers __exit_signal/__cleanup_sighand to
- * release_task() for the same reason. */
- if (task->signal) {
- put_signal_struct(task->signal);
- task->signal = nullptr;
- }
- if (task->sighand) {
- put_sighand(task->sighand);
- task->sighand = nullptr;
- }
-
- /* Free kernel stack */
- if (task->kstack_pa != 0) {
- pmm_free_pages(pa_to_page(phys_addr(task->kstack_pa)), 1);
- }
-
- /* Free TCB */
- delete task;
-}
-
-/* ======================================================================
- * do_wait4 — wait for a child to exit (Linux-compatible)
- *
- * @wait_pid: >0 = specific child PID
- * -1 = any child
- * 0 = any child in same process group (TODO)
- * <-1 = any child in specific process group (TODO)
- * @options: WNOHANG = return immediately if no zombie
- * @exit_code: output parameter (already in Linux wstatus format)
- *
- * Returns: PID of exited child, -ECHILD, or -EAGAIN (WNOHANG).
- * ====================================================================== */
-
-int32 do_wait4(int32 wait_pid, int options, int32* exit_code)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) return -EINVAL;
-
- while (true) {
- TaskControlBlock* zombie = nullptr;
- TaskControlBlock* report = nullptr; /* Stopped/Continued report */
- int32 report_status = 0;
- bool has_children = false;
-
- /*
- * Two-phase wait: put ourselves on the wait queue BEFORE
- * checking the condition. This closes the race window where
- * a child exits between our check and schedule().
- *
- * If a child exits and calls wake_all() while we're checking,
- * our state will be flipped back to Runnable, and schedule()
- * will simply return immediately.
- */
- curr->wait_chld.prepare_to_wait(curr, TaskState::Sleeping);
-
- /* Scan children */
- curr->lock.acquire();
-
- if (curr->children.is_empty()) {
- curr->lock.release();
- curr->wait_chld.finish_wait(curr);
- return -ECHILD;
- }
- has_children = true;
-
- list_node* node = curr->children.next;
- while (node != &curr->children) {
- auto* child = list_entry<TaskControlBlock, &TaskControlBlock::sibling>(node);
- bool matches = (wait_pid == -1) || (wait_pid == child->pid);
- if (!matches) {
- node = node->next;
- continue;
- }
-
- /* Zombie reaping has priority — it frees resources. */
- if (child->state == TaskState::Zombie) {
- zombie = child;
- break;
- }
-
- /* WUNTRACED: report Stopped child once per stop event. */
- if ((options & WUNTRACED) && child->stopped_code != 0) {
- report = child;
- report_status = child->stopped_code;
- break;
- }
-
- /* WCONTINUED: report a one-shot SIGCONT resume. */
- if ((options & WCONTINUED) && child->notify_continued) {
- report = child;
- report_status = W_CONTINUED();
- break;
- }
-
- node = node->next;
- }
- curr->lock.release();
-
- if (zombie) {
- curr->wait_chld.finish_wait(curr);
- int32 pid = zombie->pid;
- if (exit_code) *exit_code = zombie->exit_code;
- task_destroy(zombie);
- return pid;
- }
-
- if (report) {
- /* Status-only report: clear the one-shot flag, leave the
- * child alive. WNOWAIT (peek) would skip the clear, but
- * we don't yet honour it. */
- curr->wait_chld.finish_wait(curr);
- int32 pid = report->pid;
- if (exit_code) *exit_code = report_status;
- if (W_CONTINUED() == report_status) {
- report->notify_continued = false;
- } else {
- report->stopped_code = 0;
- }
- return pid;
- }
-
- /* No event to report */
- if (!has_children) {
- curr->wait_chld.finish_wait(curr);
- return -ECHILD;
- }
- if (options & WNOHANG) {
- curr->wait_chld.finish_wait(curr);
- return 0;
- }
-
- /* Condition still false — deschedule.
- * We are already on the wait queue and marked Sleeping. */
- schedule();
-
- /* Woken up — finish_wait removes us from queue, restores Runnable */
- curr->wait_chld.finish_wait(curr);
-
- /* Loop back to re-check the condition */
- }
-}
-
-/* Legacy wrapper for internal callers */
-int32 task_wait(int32* exit_code)
-{
- return do_wait4(-1, 0, exit_code);
-}
-
-/* ======================================================================
- * task_clone — clone current task with Linux-compatible flags
- *
- * @tf: trap frame to copy to child
- * @clone_flags: CLONE_VM | CLONE_THREAD | CSIGNAL etc.
- * @child_stack: if non-zero, set as child's user SP (for pthread)
- * @parent_tidptr: write child tid here in parent (CLONE_PARENT_SETTID)
- * @child_tidptr: write child tid here in child (CLONE_CHILD_SETTID)
- * @tls: TLS pointer (CLONE_SETTLS → tp register)
- *
- * Returns: child PID in parent, 0 in child, or negative errno.
- * ====================================================================== */
-
-int32 task_clone(arch::TrapFrame* tf, uint64 clone_flags,
- uint64 child_stack, uint64 parent_tidptr,
- uint64 child_tidptr, uint64 tls)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) return -EINVAL;
-
- TaskControlBlock* child = alloc_task("cloned", curr->priority, 0,
- static_cast<uint8>(arch::cpu::id()));
- if (!child) return -ENOMEM;
-
- /* Allocate PID */
- child->pid = g_pid_alloc.alloc();
- if (child->pid < 0) {
- pmm_free_pages(pa_to_page(phys_addr(child->kstack_pa)), 1);
- delete child;
- return -ENOMEM;
- }
-
- /* exit_signal + tgid: from clone_flags.
- * Linux: fork.c line 2418/2424 */
- if (clone_flags & CLONE_THREAD) {
- child->tgid = curr->tgid;
- child->exit_signal = -1; /* Linux: sub-threads auto-reap */
- } else {
- child->tgid = child->pid;
- child->exit_signal = static_cast<int32>(clone_flags & 0xFF); /* CSIGNAL */
- if (child->exit_signal == 0) {
- child->exit_signal = SIGCHLD; /* default for fork() */
- }
- }
-
- /* Address space: CLONE_VM shares, otherwise deep copy */
- if (clone_flags & CLONE_VM) {
- if (curr->mm) {
- curr->mm->incref();
- child->mm = curr->mm;
- }
- } else {
- if (curr->mm) {
- child->mm = vmm_clone(curr->mm);
- if (!child->mm) {
- g_pid_alloc.free(child->pid);
- pmm_free_pages(pa_to_page(phys_addr(child->kstack_pa)), 1);
- delete child;
- return -ENOMEM;
- }
- }
- }
-
- /* Copy TrapFrame to the child's kernel stack */
- uint64 tf_addr = child->kstack_va + kKernelStackSize - sizeof(arch::TrapFrame);
- child->trapframe = reinterpret_cast<arch::TrapFrame*>(tf_addr);
- *child->trapframe = *tf;
-
- /* Set Kernel SP */
- child->trapframe->kernel_sp = reinterpret_cast<uint64>(child->trapframe);
- child->trapframe->kernel_cpu_id = child->cpu_id;
-
- /* Child returns 0 from fork/clone */
- child->trapframe->a[0] = 0;
-
- /* If caller specified a child stack, use it (pthread_create) */
- if (child_stack != 0) {
- child->trapframe->sp = child_stack;
- }
-
- /* CLONE_SETTLS: set thread pointer register */
- if (clone_flags & CLONE_SETTLS) {
- child->trapframe->tp = tls;
- }
-
- /* Set up context for ret_from_fork */
- uint64 stack_top = tf_addr;
- child->context = {};
- child->context.ra = reinterpret_cast<uint64>(ret_from_fork);
- child->context.sp = stack_top;
- child->context.s[0] = tf_addr;
-
- /* ---- Copy fs_struct (Linux: copy_fs in fork.c) ----
- * CLONE_FS: share the same fs_struct (increment refcount).
- * Otherwise: allocate a new fs_struct copying root/pwd. */
- if (curr->fs != nullptr) {
- if (clone_flags & CLONE_FS) {
- curr->fs->count.fetch_add(1, __ATOMIC_RELAXED);
- child->fs = curr->fs;
- } else {
- child->fs = copy_fs_struct(curr->fs);
- }
- }
-
- /* ---- Copy files_struct (Linux: copy_files in fork.c) ----
- * CLONE_FILES: share the same fd table.
- * Otherwise: duplicate the fd table. */
- if (curr->files != nullptr) {
- if (clone_flags & CLONE_FILES) {
- curr->files->count.fetch_add(1, __ATOMIC_RELAXED);
- child->files = curr->files;
- } else {
- child->files = dup_fd(curr->files);
- }
- }
-
- /* ---- Copy sighand (Linux kernel/fork.c:copy_sighand) ----
- * CLONE_SIGHAND: share signal handlers (incref parent's).
- * Otherwise: deep copy of the action table; child gets its own
- * sighand_struct (and its own siglock). Both branches MUST yield
- * a non-null sighand — every user task needs one so signal paths
- * can take the siglock without lazy-allocation races. */
- if (clone_flags & CLONE_SIGHAND) {
- if (!curr->sighand) {
- /* CLONE_SIGHAND requires the parent to have a sighand. */
- return -EINVAL;
- }
- get_sighand(curr->sighand);
- child->sighand = curr->sighand;
- } else {
- child->sighand = copy_sighand(curr->sighand);
- if (!child->sighand) {
- /* OOM — same best-effort policy as fs/files/signal above.
- * A separate cleanup-on-OOM pass is on the to-do list. */
- }
- }
-
- /* ---- Copy signal_struct (Linux: copy_signal in fork.c) ----
- * CLONE_THREAD shares the parent's signal_struct (incref).
- * Otherwise allocate a new one that inherits pgid/sid/tty from the
- * parent so the child stays in the same process group and session —
- * this is what makes fork() participate in the existing job-control
- * identity without explicit setpgid() calls. */
- if (curr->signal != nullptr) {
- if (clone_flags & CLONE_THREAD) {
- get_signal_struct(curr->signal);
- child->signal = curr->signal;
- } else {
- child->signal = fork_signal_struct(curr->signal, child);
- }
- }
-
- /* Add to task tree */
- child->parent = curr;
- {
- LockGuard guard(curr->lock);
- list_node* head = &curr->children;
- child->sibling.prev = head->prev;
- child->sibling.next = head;
- head->prev->next = &child->sibling;
- head->prev = &child->sibling;
- }
-
- /* Add to global list and enqueue */
- task_list_add(child);
- sched_enqueue(child);
-
- /* CLONE_PARENT_SETTID: write child tid to parent's user address */
- if ((clone_flags & CLONE_PARENT_SETTID) && parent_tidptr != 0) {
- copy_to_user(parent_tidptr, &child->pid, sizeof(child->pid));
- }
-
- /* CLONE_CHILD_SETTID: write child tid into child's user address.
- * Note: this runs in parent context, but the address is in the
- * child's mm. For CLONE_VM (threads), parent and child share mm
- * so this works directly. For fork, the page was just CoW-copied. */
- if ((clone_flags & CLONE_CHILD_SETTID) && child_tidptr != 0) {
- if (clone_flags & CLONE_VM) {
- /* Same address space — write directly */
- copy_to_user(child_tidptr, &child->pid, sizeof(child->pid));
- }
- /* For non-CLONE_VM fork, the child's mm is separate.
- * The write would need to go into the child's pagetable.
- * Linux handles this in the child's first schedule via
- * a special flag. For now, threads (CLONE_VM) are the
- * primary consumer. */
- }
-
- /* CLONE_CHILD_CLEARTID: kernel writes 0 to child_tidptr on exit */
- if (clone_flags & CLONE_CHILD_CLEARTID) {
- child->clear_child_tid = child_tidptr;
- }
-
- return child->pid;
-}
-
-/* Legacy wrapper — plain fork() semantics */
-int32 task_fork(arch::TrapFrame* tf)
-{
- return task_clone(tf, SIGCHLD, 0, 0, 0, 0);
-}
-
-
-/* ======================================================================
- * task_check_stack_canary
- * ====================================================================== */
-
-void task_check_stack_canary(TaskControlBlock* task)
-{
- if (!task || task->kstack_va == 0) return;
-
- auto* canary = reinterpret_cast<uint64*>(task->kstack_va);
- if (*canary != kStackCanary) {
- kernel_panic("kernel stack overflow detected");
- }
-}
diff --git a/na/kernel/timer.cc b/na/kernel/timer.cc
deleted file mode 100644
index 3cf03cf..0000000
--- a/na/kernel/timer.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <lume/timer.h>
-#include <lume/fdt.h>
-#include <lume/sched.h>
-#include <arch/cpu.h>
-#include <arch/sbi.h>
-
-#include <kernel/sync/spinlock.h>
-
-uint64 g_jiffies = 0;
-static uint64 g_timer_freq = 10000000;
-static uint64 g_tick_interval = 0;
-
-static list_node g_timer_list;
-static Spinlock g_timer_lock;
-
-
-void add_timer(TimerList* timer) {
- LockGuard guard(g_timer_lock);
-
- list_node* pos;
- list_for_each(pos, &g_timer_list) {
- TimerList* t = list_entry<TimerList, &TimerList::entry>(pos);
- if (t->expires > timer->expires) {
- break;
- }
- }
-
- timer->entry.prev = pos->prev;
- timer->entry.next = pos;
- pos->prev->next = &timer->entry;
- pos->prev = &timer->entry;
-}
-
-int del_timer(TimerList* timer) {
- LockGuard guard(g_timer_lock);
- if (timer->entry.next != nullptr && timer->entry.next != &timer->entry) {
- timer->entry.prev->next = timer->entry.next;
- timer->entry.next->prev = timer->entry.prev;
- timer->entry.init();
- return 1;
- }
- return 0;
-}
-
-int mod_timer(TimerList* timer, uint64 expires) {
- int ret = del_timer(timer);
- timer->expires = expires;
- add_timer(timer);
- return ret;
-}
-
-static void run_timers() {
- g_timer_lock.acquire();
-
- while (!g_timer_list.is_empty()) {
- TimerList* t = list_entry<TimerList, &TimerList::entry>(g_timer_list.next);
- if (t->expires > g_jiffies) {
- break;
- }
-
- t->entry.prev->next = t->entry.next;
- t->entry.next->prev = t->entry.prev;
- t->entry.init();
-
- g_timer_lock.release();
- t->function(t);
- g_timer_lock.acquire();
- }
-
- g_timer_lock.release();
-}
-
-static void timer_set_next() {
- arch::sbi::set_timer(arch::cpu::read_time() + g_tick_interval);
-}
-
-void timer_init() {
- DeviceNode* cpus = fdt_get_node_by_path("/cpus");
- if (cpus) {
- g_timer_freq = cpus->get_prop_u32("timebase-frequency", g_timer_freq);
- }
-
- // Default 100 Hz (10ms per tick)
- g_tick_interval = g_timer_freq / 100;
-
- g_timer_lock.init("timer_list");
- g_timer_list.init();
-
- timer_set_next();
-
- arch::cpu::timer_intr_on();
-}
-
-
-void timer_init_ap() {
- timer_set_next();
- arch::cpu::timer_intr_on();
-}
-
-extern "C" uint32 bsp_hart_id;
-
-void timer_tick() {
- if (arch::cpu::id() == bsp_hart_id) {
- g_jiffies++;
- run_timers();
- }
-
- timer_set_next();
- sched_tick();
-}
-
-
-uint64 timer_get_freq() {
- return g_timer_freq;
-}
diff --git a/na/kernel/trap.cc b/na/kernel/trap.cc
deleted file mode 100644
index 2116d91..0000000
--- a/na/kernel/trap.cc
+++ /dev/null
@@ -1,258 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Trap MI Layer — Machine-Independent Semantic Dispatcher
- *
- * Receives a translated TrapCause from the MD layer and routes to
- * the appropriate handler.
- *
- * *** MI INVARIANT: This file contains ZERO inline assembly,
- * ZERO arch-specific constants (no CSR reads, no scause codes). ***
- * arch/trapframe.h is included because TrapFrame is the cross-layer
- * data contract defined in docs/specs/arch_abstraction.md §3.2.
- *
- * Reference: docs/specs/trap.md §4
- */
-
-#include <arch/cpu.h>
-#include <lume/trap.h>
-#include <lume/irq.h>
-#include <lume/console.h>
-#include <lume/kprintf.h>
-#include <lume/errno.h>
-#include <lume/sched.h>
-#include <lume/timer.h>
-#include <lume/klog.h>
-#include <lume/syscall.h>
-#include <lume/task.h>
-#include <lume/vmm.h>
-#include <lume/signal.h>
-#include <lume/signal_nr.h>
-#include <lume/smp.h>
-#include <arch/trapframe.h> /* TrapFrame: cross-layer data contract */
-#include <arch/extable.h> /* search_extable: kernel fault recovery */
-
-/* ========================================================================
- * trap_handle() — MI-layer dispatcher for traps
- *
- * Called by MD layer after scause → TrapInfo translation.
- * ======================================================================== */
-
-static TrapStats g_trap_stats = {};
-
-static uint32 trap_cause_index(TrapCause cause)
-{
- uint32 raw = static_cast<uint32>(cause);
-
- /* Exceptions 0-7 map directly to indices 0-7 */
- if (raw <= 7) {
- return raw;
- }
- /* IRQs 16-18 map to indices 8-10 */
- if (raw >= 16 && raw <= 18) {
- return raw - 16 + 8;
- }
- /* Unknown / out-of-range */
- return 11;
-}
-
-static void trap_record(const TrapInfo& info)
-{
- uint32 idx = trap_cause_index(info.cause);
- __atomic_fetch_add(&g_trap_stats.total, 1, __ATOMIC_RELAXED);
- __atomic_fetch_add(&g_trap_stats.by_cause[idx], 1, __ATOMIC_RELAXED);
- g_trap_stats.last_cause = info.cause;
- g_trap_stats.last_class = info.cls;
- g_trap_stats.last_origin = info.origin;
- g_trap_stats.last_val = info.val;
- g_trap_stats.last_epc = info.epc;
- g_trap_stats.last_scause = info.scause;
-}
-
-static VmFaultCause vm_fault_cause_from_trap(TrapCause cause)
-{
- switch (cause) {
- case TrapCause::PageFaultStore:
- return VmFaultCause::Write;
- case TrapCause::PageFaultExec:
- return VmFaultCause::Exec;
- case TrapCause::PageFaultLoad:
- default:
- return VmFaultCause::Read;
- }
-}
-
-void trap_stats_get(TrapStats* out)
-{
- if (!out) {
- return;
- }
- *out = g_trap_stats;
-}
-
-void trap_stats_reset()
-{
- g_trap_stats = {};
-}
-
-void trap_handle(arch::TrapFrame* tf, const TrapInfo& info)
-{
- trap_record(info);
-
- /* ---- Kernel exception handling ----
- * Interrupts are always allowed through. For exceptions:
- * - Page faults: handled in the switch below (extable fixup path)
- * - Other exceptions: try extable, then panic.
- *
- * This replaces the old blanket kernel_panic, enabling
- * copy_from_user/copy_to_user to safely fault on user pages. */
- if (info.origin == TrapOrigin::FromKernel &&
- info.cls != TrapClass::Interrupt) {
-
- /* Page faults are dispatched in the switch — let them through */
- bool is_page_fault =
- (info.cause == TrapCause::PageFaultLoad ||
- info.cause == TrapCause::PageFaultStore ||
- info.cause == TrapCause::PageFaultExec);
-
- if (!is_page_fault) {
- /* Non-page-fault kernel exception: try extable fixup */
- uint64 fixup = search_extable(info.epc);
- if (fixup != 0) {
- tf->epc = fixup;
- return;
- }
- kprintf("[trap] fatal kernel exception: cause=%u epc=0x%llx val=0x%llx\n",
- static_cast<unsigned>(info.cause), info.epc, info.val);
- kernel_panic("unrecoverable kernel exception");
- }
- }
-
- switch (info.cause) {
- /* ---- Page Faults ---- */
- case TrapCause::PageFaultLoad:
- case TrapCause::PageFaultStore:
- case TrapCause::PageFaultExec:
- if (info.origin == TrapOrigin::FromUser) {
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->mm) {
- kernel_panic("user page fault with no current mm");
- }
-
- int ret = vmm_handle_page_fault(curr->mm, info.val,
- vm_fault_cause_from_trap(info.cause));
- if (ret == -EFAULT) {
- kprintf("[trap] user SIGSEGV (MAPERR) epc=0x%llx addr=0x%llx\n",
- info.epc, info.val);
- force_sig_fault(SIGSEGV, 1 /* SEGV_MAPERR */, info.val);
- } else if (ret == -ENOMEM) {
- kprintf("[trap] user SIGKILL (OOM) epc=0x%llx addr=0x%llx\n",
- info.epc, info.val);
- force_sig_fault(SIGKILL, 0, info.val);
- } else if (ret < 0) {
- kprintf("[trap] user SIGSEGV (ACCERR) epc=0x%llx addr=0x%llx\n",
- info.epc, info.val);
- force_sig_fault(SIGSEGV, 2 /* SEGV_ACCERR */, info.val);
- }
- break;
- }
-
- /* Kernel page fault — try exception table fixup.
- * This is the path used by copy_from_user/copy_to_user when
- * accessing unmapped or faulting user addresses from S-mode. */
- {
- uint64 fixup = search_extable(info.epc);
- if (fixup != 0) {
- tf->epc = fixup;
- break;
- }
- }
- kprintf("[trap] kernel page fault: epc=0x%llx addr=0x%llx\n",
- info.epc, info.val);
- {
- TaskControlBlock* curr = current_task();
- if (curr) {
- kprintf("[trap] pid=%d state=%d\n",
- curr->pid, static_cast<int>(curr->state));
- kprintf("[trap] tf->epc=%llx tf->ra=%llx tf->sp=%llx\n",
- tf->epc, tf->ra, tf->sp);
- }
- }
- kernel_panic("unhandled kernel page fault (no extable entry)");
- break;
-
- /* ---- System Call ---- */
- case TrapCause::Syscall:
- syscall_dispatch_md(tf);
- break;
-
- /* ---- Interrupts ---- */
- case TrapCause::IrqTimer:
- /* Timer tick */
- timer_tick();
- break;
-
- case TrapCause::IrqExternal: {
- /* External interrupt: generic claim → dispatch → complete */
- uint32 irq = g_irq_manager.claim();
- if (irq != 0) {
- g_irq_manager.dispatch(irq);
- g_irq_manager.complete(irq);
- }
- break;
- }
-
- case TrapCause::IrqSoftware:
- /* S-mode software interrupt = IPI from another hart */
- smp_handle_ipi();
- break;
-
- /* ---- Fatal exceptions ---- */
- case TrapCause::IllegalInst:
- if (info.origin == TrapOrigin::FromUser) {
- kprintf("[trap] user illegal instruction at epc=0x%llx stval=0x%llx\n",
- info.epc, info.val);
- force_sig_fault(SIGILL, 1 /* ILL_ILLOPC */, info.epc);
- } else {
- kprintf("[trap] illegal instruction at epc=0x%llx\n", info.epc);
- kernel_panic("illegal instruction in kernel mode");
- }
- break;
-
- case TrapCause::AccessFault:
- kprintf("[trap] access fault\n");
- kernel_panic("access fault");
- break;
-
- case TrapCause::AlignFault:
- kprintf("[trap] alignment fault\n");
- kernel_panic("alignment fault");
- break;
-
- case TrapCause::Breakpoint:
- kprintf("[trap] breakpoint\n");
- /* ebreak advance is handled by MD layer (arch-specific instruction size) */
- break;
-
- case TrapCause::Unknown:
- default:
- kprintf("[trap] unknown trap\n");
- kernel_panic("unknown trap");
- break;
- }
-
- /* Process pending signals before returning to user mode */
- if (info.origin == TrapOrigin::FromUser) {
- do_signal(tf);
- }
-
- /* Preemption check on return from any interrupt.
- * This is the kernel's equivalent of Linux's retint_kernel path:
- * if a timer tick (or IPI) set need_resched, we must call schedule()
- * before returning — otherwise the idle loop never yields to
- * runnable tasks like PID 1. */
- if (info.cls == TrapClass::Interrupt) {
- sched_check_preempt();
- }
-}
diff --git a/na/kernel/uaccess.cc b/na/kernel/uaccess.cc
deleted file mode 100644
index 14c5fb7..0000000
--- a/na/kernel/uaccess.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * User-Kernel Memory Copy — Phase 3 (Exception Tables)
- *
- * Validates user pointers by checking they fall within user address space
- * (below KERNEL_VA_BASE). Uses assembly-level safe copy functions that
- * integrate with the kernel's Exception Table mechanism.
- *
- * Reference: docs/specs/syscall.md §8
- */
-
-#include <lume/uaccess.h>
-#include <lume/task.h>
-#include <lume/errno.h>
-#include <lume/config.h>
-#include <lume/mm_layout.h>
-
-extern "C" int __uaccess_memcpy(void* dst, const void* src, uint64 len);
-extern "C" int64 __uaccess_strncpy(char* dst, const char* src, uint64 max_len);
-
-int copy_from_user(void* kernel_dst, uint64 user_src, uint64 len)
-{
- if (len == 0) return 0;
- if (!is_user_range(user_src, len)) return -EFAULT;
-
- return __uaccess_memcpy(kernel_dst, reinterpret_cast<const void*>(user_src), len);
-}
-
-int copy_to_user(uint64 user_dst, const void* kernel_src, uint64 len)
-{
- if (len == 0) return 0;
- if (!is_user_range(user_dst, len)) return -EFAULT;
-
- return __uaccess_memcpy(reinterpret_cast<void*>(user_dst), kernel_src, len);
-}
-
-int64 strncpy_from_user(char* kernel_dst, uint64 user_src, uint64 max_len)
-{
- if (!is_user_range(user_src, 1)) return -EFAULT;
-
- /* We must also check that the string does not cross into kernel space.
- This is safely handled by the assembly routine which will fault if it hits
- an unmapped page. However, to strictly respect kKernelVaBase, we should
- restrict max_len to the distance to kKernelVaBase. */
- uint64 max_allowed = kKernelVaBase - user_src;
- if (max_len > max_allowed) {
- max_len = max_allowed;
- }
-
- return __uaccess_strncpy(kernel_dst, reinterpret_cast<const char*>(user_src), max_len);
-}
diff --git a/na/kernel/waitqueue.cc b/na/kernel/waitqueue.cc
deleted file mode 100644
index 9689417..0000000
--- a/na/kernel/waitqueue.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * WaitQueue — blocking synchronization implementation
- *
- * Two-phase wait protocol:
- *
- * prepare_to_wait(): task → Sleeping, link into queue
- * [caller checks condition — if still false, calls schedule()]
- * finish_wait(): remove from queue, restore Runnable
- *
- * Legacy sleep() wraps the two-phase pattern for simple callers.
- *
- * wake_one()/wake_all(): dequeue waiters → Runnable, sched_enqueue()
- *
- * The internal spinlock protects the wait list only.
- * schedule() is called AFTER releasing all spinlocks to avoid
- * holding a lock across a context switch.
- */
-
-#include <lume/waitqueue.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-#include <lume/kprintf.h>
-#include <arch/cpu.h>
-
-/* ------------------------------------------------------------------ */
-/* Two-phase wait API */
-/* ------------------------------------------------------------------ */
-
-void WaitQueue::prepare_to_wait(TaskControlBlock* task, TaskState new_state)
-{
- LockGuard guard(lock_);
-
- /* Set state BEFORE linking — this is the key to preventing lost wakeups.
- * If wake_one fires between now and schedule(), it will set state back
- * to Runnable, and schedule() will see Runnable and not block. */
- task->state = new_state;
-
- /* Only add to queue if not already linked (idempotent) */
- if (task->run_link.next == &task->run_link) {
- /* Link into wait queue tail */
- list_node* node = &task->run_link;
- node->prev = head_.prev;
- node->next = &head_;
- head_.prev->next = node;
- head_.prev = node;
- }
-}
-
-void WaitQueue::finish_wait(TaskControlBlock* task)
-{
- LockGuard guard(lock_);
-
- /* Remove from wait queue if still linked */
- if (task->run_link.next != &task->run_link) {
- list_node* node = &task->run_link;
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->init();
- }
-
- /* Ensure task is Runnable (it may have been woken by wake_one/wake_all
- * already, or it may never have slept if the condition was true). */
- if (task->state == TaskState::Sleeping) {
- task->state = TaskState::Runnable;
- }
-}
-
-/* ------------------------------------------------------------------ */
-/* Legacy convenience API */
-/* ------------------------------------------------------------------ */
-
-void WaitQueue::sleep(Spinlock* external_lock)
-{
- TaskControlBlock* curr = current_task();
- if (!curr) {
- if (external_lock) external_lock->release();
- return;
- }
-
- /* Phase 1: add to queue, mark Sleeping */
- prepare_to_wait(curr, TaskState::Sleeping);
-
- /* Release external lock AFTER we are on the queue (critical ordering) */
- if (external_lock) {
- external_lock->release();
- }
-
- /* Phase 2: context switch away — will return here when woken */
- schedule();
-
- /* Phase 3: clean up */
- finish_wait(curr);
-}
-
-/* ------------------------------------------------------------------ */
-/* Wakeup */
-/* ------------------------------------------------------------------ */
-
-bool WaitQueue::wake_one()
-{
- LockGuard guard(lock_);
-
- if (head_.is_empty())
- return false;
-
- /* Dequeue the first (oldest) waiter */
- list_node* node = head_.next;
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->init();
-
- TaskControlBlock* task =
- list_entry<TaskControlBlock, &TaskControlBlock::run_link>(node);
-
- task->state = TaskState::Runnable;
- sched_enqueue(task);
-
- return true;
-}
-
-int WaitQueue::wake_all()
-{
- LockGuard guard(lock_);
-
- int count = 0;
- while (!head_.is_empty()) {
- list_node* node = head_.next;
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->init();
-
- TaskControlBlock* task =
- list_entry<TaskControlBlock, &TaskControlBlock::run_link>(node);
-
- task->state = TaskState::Runnable;
- sched_enqueue(task);
- count++;
- }
-
- return count;
-}
diff --git a/na/lib/cxx_abi.cc b/na/lib/cxx_abi.cc
deleted file mode 100644
index 872b4a2..0000000
--- a/na/lib/cxx_abi.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * cxx_abi.cc — Minimal C++ ABI stubs for freestanding kernel
- *
- * Provides symbols that the compiler/linker expects from the C++
- * runtime library. All error paths are fatal (kernel_panic).
- *
- * Reference: docs/specs/cpp_runtime.md
- */
-
-#include <lume/klog.h>
-
-/* Pure virtual function call trap.
- * Generated by the compiler in vtable entries for pure virtual methods.
- * Reaching this means a derived class failed to implement a pure virtual. */
-extern "C" void __cxa_pure_virtual()
-{
- kernel_panic("pure virtual function call");
-}
-
-/* Global destructor registration.
- * The kernel does not support global object destruction — all global
- * objects use Placement New with explicit lifetime management.
- * If the compiler emits a call to this, it indicates a code violation. */
-extern "C" int __cxa_atexit(void (*)(void*), void*, void*)
-{
- kernel_panic("__cxa_atexit called in kernel");
- return 0; // unreachable
-}
-
-/* DSO handle — required by __cxa_atexit ABI.
- * The kernel is statically linked; this symbol just needs to exist. */
-extern "C" {
-void* __dso_handle __attribute__((visibility("hidden"))) = nullptr;
-}
diff --git a/na/lib/fdt.c b/na/lib/fdt.c
deleted file mode 100644
index 1b20383..0000000
--- a/na/lib/fdt.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <lume/libfdt_env.h>
-#include "../scripts/dtc/libfdt/fdt.c"
diff --git a/na/lib/fdt_addresses.c b/na/lib/fdt_addresses.c
deleted file mode 100644
index d58f896..0000000
--- a/na/lib/fdt_addresses.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <lume/libfdt_env.h>
-#include "../scripts/dtc/libfdt/fdt_addresses.c"
diff --git a/na/lib/fdt_empty_tree.c b/na/lib/fdt_empty_tree.c
deleted file mode 100644
index 37f8ef8..0000000
--- a/na/lib/fdt_empty_tree.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <lume/libfdt_env.h>
-#include "../scripts/dtc/libfdt/fdt_empty_tree.c"
diff --git a/na/lib/fdt_ro.c b/na/lib/fdt_ro.c
deleted file mode 100644
index 3599d49..0000000
--- a/na/lib/fdt_ro.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <lume/libfdt_env.h>
-#include "../scripts/dtc/libfdt/fdt_ro.c"
diff --git a/na/lib/fdt_rw.c b/na/lib/fdt_rw.c
deleted file mode 100644
index 65d7a71..0000000
--- a/na/lib/fdt_rw.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <lume/libfdt_env.h>
-#include "../scripts/dtc/libfdt/fdt_rw.c"
diff --git a/na/lib/fdt_strerror.c b/na/lib/fdt_strerror.c
deleted file mode 100644
index dae35ea..0000000
--- a/na/lib/fdt_strerror.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <lume/libfdt_env.h>
-#include "../scripts/dtc/libfdt/fdt_strerror.c"
diff --git a/na/lib/fdt_sw.c b/na/lib/fdt_sw.c
deleted file mode 100644
index d04f220..0000000
--- a/na/lib/fdt_sw.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <lume/libfdt_env.h>
-#include "../scripts/dtc/libfdt/fdt_sw.c"
diff --git a/na/lib/fdt_wip.c b/na/lib/fdt_wip.c
deleted file mode 100644
index ebfb3c1..0000000
--- a/na/lib/fdt_wip.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include <lume/libfdt_env.h>
-#include "../scripts/dtc/libfdt/fdt_wip.c"
diff --git a/na/lib/klog.cc b/na/lib/klog.cc
deleted file mode 100644
index 86c67f8..0000000
--- a/na/lib/klog.cc
+++ /dev/null
@@ -1,574 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * klog.cc — Kernel logging subsystem implementation
- *
- * Implements the ring buffer, spinlock-protected writes, log level
- * filtering, and synchronous console output.
- *
- * Phase 2 TODO: async flush via kernel thread (see docs/specs/klog.md)
- */
-
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/console.h>
-#include <lume/atomic.h>
-#include <kernel/sync/spinlock.h>
-#include <arch/cpu.h>
-
-/* Forward declaration — mpaland/printf */
-extern "C" int vsnprintf_(char* buffer, size_t count,
- const char* format, __builtin_va_list va);
-
-/* ========================================================================
- * Ring Buffer State
- * ======================================================================== */
-
-struct KlogEntryHdr {
- uint32 seq;
- uint32 len;
- uint64 ts;
- uint16 cpu;
- uint8 level;
- uint8 subsys;
- uint16 reserved;
-};
-
-static struct {
- char data[KLOG_BUF_SIZE];
- uint32 head; /* Next write position */
- uint32 tail; /* Next read position (for /proc/kmsg) */
- uint32 console_tail; /* Next position to flush to console */
- uint32 dropped;
- lume::atomic<uint32> seq;
- uint64 subsys_mask;
- Spinlock lock;
- KlogLevel console_level; /* Messages <= this level go to UART */
- bool initialized;
- bool panic_mode;
-} g_klog;
-
-/* ========================================================================
- * Level name table
- * ======================================================================== */
-
-static const char* klog_level_name(KlogLevel level)
-{
- static const char* names[] = {
- "EMERG", "ALERT", "CRIT", "ERR",
- "WARN", "NOTICE", "INFO", "DEBUG"
- };
- if (level <= KLOG_DEBUG) {
- return names[level];
- }
- return "?";
-}
-
-static const char* klog_subsys_name(KlogSubsys subsys)
-{
- static const char* names[] = {
- "GEN",
- "SCHED",
- "TRAP",
- "IRQ",
- "TIMER",
- "VMM",
- "PMM",
- "SLAB",
- "FS",
- "DRV",
- "TEST",
- };
- if (subsys < KLOG_SUBSYS_MAX) {
- return names[subsys];
- }
- return "?";
-}
-
-static const char* parse_kern_level(const char* fmt, KlogLevel* level)
-{
- if (!fmt || !level) {
- return fmt;
- }
-
- /* Linux-style "\001n" prefix (KERN_SOH) */
- if (fmt[0] == '\001' && fmt[1] >= '0' && fmt[1] <= '7') {
- *level = static_cast<KlogLevel>(fmt[1] - '0');
- return fmt + 2;
- }
-
- /* Alternate "<n>" prefix */
- if (fmt[0] == '<' && fmt[1] >= '0' && fmt[1] <= '7' && fmt[2] == '>') {
- *level = static_cast<KlogLevel>(fmt[1] - '0');
- return fmt + 3;
- }
-
- return fmt;
-}
-
-/* ========================================================================
- * Ring Buffer Operations
- * ======================================================================== */
-
-/* Write a byte to the ring buffer. Overwrites oldest data on overflow. */
-static inline uint32 min_u32(uint32 a, uint32 b)
-{
- return (a < b) ? a : b;
-}
-
-static void ring_write_bytes(const void* src, uint32 len)
-{
- const char* p = reinterpret_cast<const char*>(src);
- for (uint32 i = 0; i < len; ++i) {
- g_klog.data[(g_klog.head + i) & (KLOG_BUF_SIZE - 1)] = p[i];
- }
- g_klog.head += len;
-}
-
-static void ring_read_bytes(uint32 pos, void* dst, uint32 len)
-{
- char* out = reinterpret_cast<char*>(dst);
- for (uint32 i = 0; i < len; ++i) {
- out[i] = g_klog.data[(pos + i) & (KLOG_BUF_SIZE - 1)];
- }
-}
-
-static void ring_drop_oldest(uint32 needed)
-{
- uint32 min_tail = min_u32(g_klog.tail, g_klog.console_tail);
-
- while (KLOG_BUF_SIZE - (g_klog.head - min_tail) < needed) {
- KlogEntryHdr hdr;
- ring_read_bytes(min_tail, &hdr, sizeof(hdr));
-
- uint32 entry = sizeof(hdr) + hdr.len;
- if (entry == 0 || entry > KLOG_BUF_SIZE) {
- min_tail++;
- g_klog.dropped++;
- continue;
- }
- min_tail += entry;
- g_klog.dropped++;
- }
-
- if (g_klog.tail < min_tail) {
- g_klog.tail = min_tail;
- }
- if (g_klog.console_tail < min_tail) {
- g_klog.console_tail = min_tail;
- }
-}
-
-/* ========================================================================
- * Console Output (synchronous)
- * ======================================================================== */
-
-static void console_emit(const char* s, int len)
-{
- console_write_all(s, len);
-}
-
-static int klog_format_line(char* out, int out_size,
- const KlogEntryHdr& hdr,
- const char* msg, int msg_len)
-{
- const char* lvl = klog_level_name(static_cast<KlogLevel>(hdr.level));
- const char* subsys = klog_subsys_name(static_cast<KlogSubsys>(hdr.subsys));
-
- int prefix = ksnprintf(out, out_size,
- "[%s][cpu%u][%llu][%s] ",
- lvl, (uint32)hdr.cpu,
- (unsigned long long)hdr.ts, subsys);
- if (prefix < 0) {
- return 0;
- }
-
- int total = prefix;
- int avail = out_size - 1 - total;
- if (avail > 0) {
- int to_copy = (msg_len < avail) ? msg_len : avail;
- for (int i = 0; i < to_copy; ++i) {
- out[total + i] = msg[i];
- }
- total += to_copy;
- }
-
- if (total > 0 && out[total - 1] != '\n' && total < out_size - 1) {
- out[total++] = '\n';
- }
-
- out[total] = '\0';
- return total;
-}
-
-static int klog_read_internal(char* buf, int size, uint32* tail_ptr, bool consume)
-{
- if (size <= 0) {
- return 0;
- }
-
- int written = 0;
- uint32 pos = *tail_ptr;
-
- while (pos != g_klog.head) {
- KlogEntryHdr hdr;
- ring_read_bytes(pos, &hdr, sizeof(hdr));
- uint32 raw_len = hdr.len;
- if (raw_len > KLOG_BUF_SIZE) {
- break;
- }
- uint32 copy_len = raw_len;
- if (copy_len > KLOG_LINE_MAX) {
- copy_len = KLOG_LINE_MAX;
- }
-
- char msg[KLOG_LINE_MAX + 1];
- ring_read_bytes(pos + sizeof(hdr), msg, copy_len);
- msg[copy_len] = '\0';
-
- char line[KLOG_LINE_MAX + 64];
- int line_len = klog_format_line(line, sizeof(line), hdr, msg,
- static_cast<int>(copy_len));
-
- if (line_len <= 0 || written + line_len > size) {
- break;
- }
-
- for (int i = 0; i < line_len; ++i) {
- buf[written + i] = line[i];
- }
- written += line_len;
-
- pos += sizeof(hdr) + raw_len;
- }
-
- if (consume) {
- *tail_ptr = pos;
- }
-
- return written;
-}
-
-static void klog_vex(KlogLevel level, KlogSubsys subsys,
- const char* fmt, __builtin_va_list va);
-
-/* ========================================================================
- * Public API
- * ======================================================================== */
-
-void klog_init(void)
-{
- g_klog.head = 0;
- g_klog.tail = 0;
- g_klog.console_tail = 0;
- g_klog.dropped = 0;
- g_klog.seq.store(0, __ATOMIC_RELAXED);
- g_klog.subsys_mask = (1ULL << KLOG_SUBSYS_MAX) - 1;
- g_klog.console_level = static_cast<KlogLevel>(KLOG_DEFAULT_LEVEL);
- g_klog.initialized = true;
- g_klog.panic_mode = false;
- g_klog.lock.init("klog");
-}
-
-void klog(KlogLevel level, const char* fmt, ...)
-{
- __builtin_va_list va;
- __builtin_va_start(va, fmt);
- klog_vex(level, KLOG_SUBSYS_GENERIC, fmt, va);
- __builtin_va_end(va);
-}
-
-void klog_ex(KlogLevel level, KlogSubsys subsys, const char* fmt, ...)
-{
- __builtin_va_list va;
- __builtin_va_start(va, fmt);
- klog_vex(level, subsys, fmt, va);
- __builtin_va_end(va);
-}
-
-static void klog_vex(KlogLevel level, KlogSubsys subsys,
- const char* fmt, __builtin_va_list va)
-{
- if (subsys >= KLOG_SUBSYS_MAX) {
- subsys = KLOG_SUBSYS_GENERIC;
- }
-
- if (g_klog.initialized && ((g_klog.subsys_mask & (1ULL << subsys)) == 0)) {
- return;
- }
-
- char msg[KLOG_LINE_MAX + 1];
- int msg_len = vsnprintf_(msg, KLOG_LINE_MAX, fmt, va);
-
- if (msg_len < 0) {
- return;
- }
- if (msg_len > KLOG_LINE_MAX) {
- msg_len = KLOG_LINE_MAX;
- }
- msg[msg_len] = '\0';
-
- KlogEntryHdr hdr;
- hdr.seq = g_klog.seq.fetch_add(1, __ATOMIC_RELAXED);
- hdr.len = static_cast<uint32>(msg_len);
- hdr.ts = arch::cpu::read_time();
- hdr.cpu = static_cast<uint16>(arch::cpu::id());
- hdr.level = static_cast<uint8>(level);
- hdr.subsys = static_cast<uint8>(subsys);
- hdr.reserved = 0;
-
- bool do_console = (!g_klog.initialized || level <= g_klog.console_level);
-
- if (g_klog.initialized && !g_klog.panic_mode) {
- LockGuard guard(g_klog.lock);
- ring_drop_oldest(sizeof(hdr) + hdr.len);
- ring_write_bytes(&hdr, sizeof(hdr));
- ring_write_bytes(msg, hdr.len);
- if (do_console) {
- g_klog.console_tail = g_klog.head;
- /* Emit to UART while holding the lock to prevent
- * interleaved output from concurrent CPUs. */
- char line[KLOG_LINE_MAX + 64];
- int line_len = klog_format_line(line, sizeof(line),
- hdr, msg, hdr.len);
- if (line_len > 0) {
- console_emit(line, line_len);
- }
- }
- } else if (do_console) {
- /* Pre-init or panic mode: emit without lock */
- char line[KLOG_LINE_MAX + 64];
- int line_len = klog_format_line(line, sizeof(line),
- hdr, msg, hdr.len);
- if (line_len > 0) {
- console_emit(line, line_len);
- }
- }
-}
-
-int vprintk(const char* fmt, __builtin_va_list va)
-{
- KlogLevel level = KLOG_INFO;
- const char* real_fmt = parse_kern_level(fmt, &level);
-
- __builtin_va_list va_copy;
- __builtin_va_copy(va_copy, va);
-
- char tmp[KLOG_LINE_MAX + 1];
- int len = vsnprintf_(tmp, KLOG_LINE_MAX, real_fmt, va_copy);
- __builtin_va_end(va_copy);
-
- klog_vex(level, KLOG_SUBSYS_GENERIC, real_fmt, va);
-
- if (len < 0) {
- return 0;
- }
- return len;
-}
-
-int printk(const char* fmt, ...)
-{
- __builtin_va_list va;
- __builtin_va_start(va, fmt);
- int ret = vprintk(fmt, va);
- __builtin_va_end(va);
- return ret;
-}
-
-void klog_set_level(KlogLevel level)
-{
- g_klog.console_level = level;
-}
-
-void klog_set_subsys_mask(uint64 mask)
-{
- g_klog.subsys_mask = mask;
-}
-
-void klog_enable_subsys(KlogSubsys subsys)
-{
- if (subsys < KLOG_SUBSYS_MAX) {
- g_klog.subsys_mask |= (1ULL << subsys);
- }
-}
-
-void klog_disable_subsys(KlogSubsys subsys)
-{
- if (subsys < KLOG_SUBSYS_MAX) {
- g_klog.subsys_mask &= ~(1ULL << subsys);
- }
-}
-
-int klog_read(char* buf, int size)
-{
- if (!g_klog.initialized || size <= 0) {
- return 0;
- }
-
- LockGuard guard(g_klog.lock);
- return klog_read_internal(buf, size, &g_klog.tail, false);
-}
-
-int klog_read_consume(char* buf, int size)
-{
- if (!g_klog.initialized || size <= 0) {
- return 0;
- }
-
- LockGuard guard(g_klog.lock);
- return klog_read_internal(buf, size, &g_klog.tail, true);
-}
-
-void klog_flush(void)
-{
- if (!g_klog.initialized) {
- return;
- }
-
- LockGuard guard(g_klog.lock);
-
- uint32 pos = g_klog.console_tail;
- while (pos != g_klog.head) {
- KlogEntryHdr hdr;
- ring_read_bytes(pos, &hdr, sizeof(hdr));
- uint32 raw_len = hdr.len;
- if (raw_len > KLOG_BUF_SIZE) {
- break;
- }
- uint32 copy_len = raw_len;
- if (copy_len > KLOG_LINE_MAX) {
- copy_len = KLOG_LINE_MAX;
- }
-
- char msg[KLOG_LINE_MAX + 1];
- ring_read_bytes(pos + sizeof(hdr), msg, copy_len);
- msg[copy_len] = '\0';
-
- char line[KLOG_LINE_MAX + 64];
- int line_len = klog_format_line(line, sizeof(line), hdr, msg,
- static_cast<int>(copy_len));
- if (line_len > 0) {
- console_emit(line, line_len);
- }
-
- pos += sizeof(hdr) + raw_len;
- }
-
- g_klog.console_tail = pos;
-}
-
-bool klog_ratelimit(KlogRateLimit* rl, uint64 now)
-{
- if (!rl) {
- return true;
- }
-
- if (rl->interval == 0 || rl->burst == 0) {
- return true;
- }
-
- if (now - rl->last_ts >= rl->interval) {
- rl->last_ts = now;
- rl->printed = 0;
- rl->suppressed = 0;
- }
-
- if (rl->printed < rl->burst) {
- rl->printed++;
- return true;
- }
-
- rl->suppressed++;
- return false;
-}
-
-void klog_stats(KlogStats* out)
-{
- if (!out) {
- return;
- }
-
- out->head = g_klog.head;
- out->tail = g_klog.tail;
- out->console_tail = g_klog.console_tail;
- out->dropped = g_klog.dropped;
- out->seq = g_klog.seq.load(__ATOMIC_RELAXED);
-}
-
-/* ========================================================================
- * Kernel Panic — Fatal Error Handler
- *
- * Dumps all critical S-mode CSRs and the stack pointer to the console,
- * then halts the CPU. Uses kprintf (NOT klog) to avoid deadlocking
- * if the panic was triggered while holding the klog spinlock.
- * ======================================================================== */
-
-extern "C" [[noreturn]] void kernel_panic(const char* msg, const char* detail) {
- /* Disable interrupts immediately */
- arch::cpu::intr_off();
-
- g_klog.panic_mode = true;
-
- /* ---- Banner ---- */
- kprintf("\n\n");
- kprintf("========================================\n");
- kprintf(" *** KERNEL PANIC ***\n");
- kprintf("========================================\n");
-
- if (msg != nullptr) {
- kprintf(" Reason : %s", msg);
- if (detail != nullptr) {
- kprintf(": %s", detail);
- }
- kprintf("\n");
- }
-
- /* ---- Read S-mode CSRs ---- */
- uint64 sepc, scause, stval, sstatus, satp, stvec, sscratch, sp_val;
-
- __asm__ volatile("csrr %0, sepc" : "=r"(sepc));
- __asm__ volatile("csrr %0, scause" : "=r"(scause));
- __asm__ volatile("csrr %0, stval" : "=r"(stval));
- __asm__ volatile("csrr %0, sstatus" : "=r"(sstatus));
- __asm__ volatile("csrr %0, satp" : "=r"(satp));
- __asm__ volatile("csrr %0, stvec" : "=r"(stvec));
- __asm__ volatile("csrr %0, sscratch" : "=r"(sscratch));
- __asm__ volatile("mv %0, sp" : "=r"(sp_val));
-
- kprintf("\n--- S-mode CSR Dump ---\n");
- kprintf(" sepc = 0x%016llx\n", sepc);
- kprintf(" scause = 0x%016llx", scause);
-
- /* Decode scause for convenience */
- bool is_interrupt = (scause >> 63) != 0;
- uint64 cause_code = scause & 0x7FFFFFFFFFFFFFFFULL;
- if (is_interrupt) {
- kprintf(" (interrupt, code=%llu)\n", cause_code);
- } else {
- kprintf(" (exception, code=%llu)\n", cause_code);
- }
-
- kprintf(" stval = 0x%016llx\n", stval);
- kprintf(" sstatus = 0x%016llx", sstatus);
- kprintf(" [SIE=%llu SPP=%llu SPIE=%llu]\n",
- (sstatus >> 1) & 1, /* SIE */
- (sstatus >> 8) & 1, /* SPP */
- (sstatus >> 5) & 1); /* SPIE */
- kprintf(" satp = 0x%016llx", satp);
- uint64 satp_mode = satp >> 60;
- kprintf(" [mode=%llu]\n", satp_mode);
- kprintf(" stvec = 0x%016llx\n", stvec);
- kprintf(" sscratch = 0x%016llx\n", sscratch);
- kprintf(" sp = 0x%016llx\n", sp_val);
-
- kprintf(" cpu_id = %llu\n", arch::cpu::id());
- kprintf(" time = %llu\n", arch::cpu::read_time());
-
- kprintf("========================================\n\n");
-
- /* Halt — spin forever with WFI to save power */
- while (true) {
- arch::cpu::halt_until_interrupt();
- }
-}
diff --git a/na/lib/kobject.cc b/na/lib/kobject.cc
deleted file mode 100644
index 8e8329e..0000000
--- a/na/lib/kobject.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * kobject — implementation.
- *
- * Lives in lib/ (mirroring Linux's lib/kobject.c) because kobject is a
- * generic primitive, not tied to the driver model.
- *
- * Reference: docs/specs/storage_stack.md §4.2
- */
-
-#include <lume/kobject.h>
-#include <lume/container_of.h>
-#include <lume/klog.h>
-
-void kobject_init(kobject* k, const kobj_type* type)
-{
- if (k == nullptr) {
- return;
- }
- k->name = nullptr;
- k->parent = nullptr;
- k->ktype = type;
- k->state_in_sysfs = false;
- k->entry.init();
- k->children.init();
- k->lock.init("kobject");
- kref_init(&k->refcount);
-}
-
-int kobject_add(kobject* k, kobject* parent, const char* name)
-{
- if (k == nullptr) {
- return -1;
- }
- k->name = name;
- if (parent != nullptr) {
- kobject_get(parent);
- k->parent = parent;
- LockGuard g(parent->lock);
- list_add_tail(&k->entry, &parent->children);
- }
- return 0;
-}
-
-void kobject_del(kobject* k)
-{
- if (k == nullptr || k->parent == nullptr) {
- return;
- }
- kobject* parent = k->parent;
- {
- LockGuard g(parent->lock);
- list_del_init(&k->entry);
- }
- k->parent = nullptr;
- kobject_put(parent);
-}
-
-void kobject_get(kobject* k)
-{
- if (k != nullptr) {
- kref_get(&k->refcount);
- }
-}
-
-static void kobject_release(kref* r)
-{
- kobject* k = container_of(r, kobject, refcount);
- if (k->ktype != nullptr && k->ktype->release != nullptr) {
- k->ktype->release(k);
- }
-}
-
-void kobject_put(kobject* k)
-{
- if (k == nullptr) {
- return;
- }
- kref_put(&k->refcount, kobject_release);
-}
diff --git a/na/lib/kprintf.cc b/na/lib/kprintf.cc
deleted file mode 100644
index a98de71..0000000
--- a/na/lib/kprintf.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * kprintf.cc — Kernel formatted output
- *
- * Uses mpaland/printf's vsnprintf_ as the core formatting engine.
- *
- * Architecture:
- * vsnprintf_() ← pure formatting, writes to buffer
- * ↓
- * kprintf() ← format + emit via console_write_all()
- * kvprintf() ← va_list variant
- * ksnprintf() ← format into caller's buffer
- *
- * Output path:
- * Pre-registration: console_write_all() → early_putc() fallback
- * Post-registration: console_write_all() → struct console chain
- */
-
-#include <lume/kprintf.h>
-#include <lume/console.h>
-#include <lume/atomic.h>
-#include <arch/cpu.h>
-#include "printf.h"
-
-/* ------------------------------------------------------------------ */
-/* Console spinlock (raw, no scheduler dependency — safe in panic) */
-/* ------------------------------------------------------------------ */
-
-static lume::atomic<uint32> g_console_lock{0};
-
-static inline void console_lock_acquire()
-{
- while (g_console_lock.exchange(1, __ATOMIC_ACQUIRE) != 0) {
- /* spin */
- }
-}
-
-static inline void console_lock_release()
-{
- g_console_lock.store(0, __ATOMIC_RELEASE);
-}
-
-/* ------------------------------------------------------------------ */
-/* _putchar: required by mpaland/printf for printf_() / vprintf_() */
-/* ------------------------------------------------------------------ */
-
-void _putchar(char character)
-{
- early_putc(character);
-}
-
-/* ------------------------------------------------------------------ */
-/* Public API */
-/* ------------------------------------------------------------------ */
-
-extern "C" int kprintf(const char* fmt, ...)
-{
- char buf[256];
- va_list va;
- va_start(va, fmt);
- int len = vsnprintf_(buf, sizeof(buf), fmt, va);
- va_end(va);
-
- /* Emit to console under lock */
- console_lock_acquire();
- console_write_all(buf, len);
- console_lock_release();
- return len;
-}
-
-extern "C" int kvprintf(const char* fmt, va_list va)
-{
- char buf[256];
- int len = vsnprintf_(buf, sizeof(buf), fmt, va);
-
- console_lock_acquire();
- console_write_all(buf, len);
- console_lock_release();
- return len;
-}
-
-extern "C" int ksnprintf(char* buf, int count, const char* fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
- int ret = vsnprintf_(buf, (size_t)count, fmt, va);
- va_end(va);
- return ret;
-}
diff --git a/na/lib/module.mk b/na/lib/module.mk
deleted file mode 100644
index 0b178f8..0000000
--- a/na/lib/module.mk
+++ /dev/null
@@ -1,66 +0,0 @@
-# lib/module.mk
-SRCS_CC += lib/cxx_abi.cc
-SRCS_CC += lib/rbtree.cc
-SRCS_CC += lib/kobject.cc
-SRCS_CC += lib/kprintf.cc
-SRCS_CC += lib/klog.cc
-SRCS_CC += lib/random.cc
-SRCS_C += lib/printf.c
-
-# ============================================================
-# musl string library (cross-platform with arch asm overrides)
-# ============================================================
-
-# Map LumeOS ARCH to musl string arch directory
-# riscv has no musl asm optimizations → pure C fallback
-MUSL_STRING_ARCH_riscv :=
-MUSL_STRING_ARCH_x86_64 := x86_64
-MUSL_STRING_ARCH_i386 := i386
-MUSL_STRING_ARCH_aarch64:= aarch64
-MUSL_STRING_ARCH_arm := arm
-
-MUSL_STRING_ARCH := $(MUSL_STRING_ARCH_$(ARCH))
-
-# Collect arch-specific asm sources (if any for this arch)
-MUSL_ASM_SRCS :=
-ifneq ($(MUSL_STRING_ARCH),)
- MUSL_ASM_SRCS += $(wildcard lib/string/$(MUSL_STRING_ARCH)/*.S) \
- $(wildcard lib/string/$(MUSL_STRING_ARCH)/*.s)
-endif
-
-# Derive which C files are overridden by asm (strip path+ext to get base name)
-MUSL_ASM_BASENAMES := $(sort $(basename $(notdir $(MUSL_ASM_SRCS))))
-
-# All C sources, then filter out those with asm replacements
-MUSL_ALL_C := $(wildcard lib/string/*.c)
-MUSL_EXCLUDED_C := $(foreach name,$(MUSL_ASM_BASENAMES),lib/string/$(name).c)
-MUSL_C_SRCS := $(filter-out $(MUSL_EXCLUDED_C),$(MUSL_ALL_C))
-
-SRCS_C += $(MUSL_C_SRCS)
-SRCS_S += $(filter %.S,$(MUSL_ASM_SRCS))
-SRCS_s += $(filter %.s,$(MUSL_ASM_SRCS))
-
-# ============================================================
-# libfdt — kernel adapter shims around scripts/dtc/libfdt/.
-#
-# Each shim is two lines:
-# #include <linux/libfdt_env.h>
-# #include "../scripts/dtc/libfdt/fdt*.c"
-# matching Linux lib/fdt*.c verbatim. The compile recipe needs:
-# - <linux/libfdt_env.h> reachable via the default -Iinclude path
-# - -I scripts/dtc/libfdt so the upstream source's #include <libfdt.h>
-# and friends resolve to scripts/dtc/libfdt/libfdt.h
-# Overriding CFLAGS_LIBC (which the %.o: %.c rule uses) avoids dragging
-# in libc-shim headers — libfdt is freestanding-compatible and uses
-# only the string functions forward-declared in <linux/libfdt_env.h>.
-# ============================================================
-LIBFDT_FILES := fdt.c fdt_ro.c fdt_wip.c fdt_rw.c fdt_sw.c \
- fdt_strerror.c fdt_empty_tree.c fdt_addresses.c
-SRCS_C += $(addprefix lib/,$(LIBFDT_FILES))
-
-# Per-target CFLAGS override: plain CFLAGS plus the upstream include dir.
-# Use `=` (recursive) not `:=` because $(CFLAGS) is defined in the top
-# Makefile AFTER this file is included — `:=` would expand it now (empty)
-# and lose -Iinclude, breaking <linux/libfdt_env.h>.
-$(addprefix $(BUILD)/lib/,$(LIBFDT_FILES:.c=.o)): CFLAGS_LIBC = \
- $(CFLAGS) -I scripts/dtc/libfdt
diff --git a/na/lib/printf.c b/na/lib/printf.c
deleted file mode 100644
index 8a700ad..0000000
--- a/na/lib/printf.c
+++ /dev/null
@@ -1,914 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// \author (c) Marco Paland (info@paland.com)
-// 2014-2019, PALANDesign Hannover, Germany
-//
-// \license The MIT License (MIT)
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
-// embedded systems with a very limited resources. These routines are thread
-// safe and reentrant!
-// Use this instead of the bloated standard/newlib printf cause these use
-// malloc for printf (and may not be thread safe).
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "printf.h"
-
-
-// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
-// printf_config.h header file
-// default: undefined
-#ifdef PRINTF_INCLUDE_CONFIG_H
-#include "printf_config.h"
-#endif
-
-
-// 'ntoa' conversion buffer size, this must be big enough to hold one converted
-// numeric number including padded zeros (dynamically created on stack)
-// default: 32 byte
-#ifndef PRINTF_NTOA_BUFFER_SIZE
-#define PRINTF_NTOA_BUFFER_SIZE 32U
-#endif
-
-// 'ftoa' conversion buffer size, this must be big enough to hold one converted
-// float number including padded zeros (dynamically created on stack)
-// default: 32 byte
-#ifndef PRINTF_FTOA_BUFFER_SIZE
-#define PRINTF_FTOA_BUFFER_SIZE 32U
-#endif
-
-// support for the floating point type (%f)
-// default: activated
-#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
-#define PRINTF_SUPPORT_FLOAT
-#endif
-
-// support for exponential floating point notation (%e/%g)
-// default: activated
-#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
-#define PRINTF_SUPPORT_EXPONENTIAL
-#endif
-
-// define the default floating point precision
-// default: 6 digits
-#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
-#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
-#endif
-
-// define the largest float suitable to print with %f
-// default: 1e9
-#ifndef PRINTF_MAX_FLOAT
-#define PRINTF_MAX_FLOAT 1e9
-#endif
-
-// support for the long long types (%llu or %p)
-// default: activated
-#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
-#define PRINTF_SUPPORT_LONG_LONG
-#endif
-
-// support for the ptrdiff_t type (%t)
-// ptrdiff_t is normally defined in <stddef.h> as long or long long type
-// default: activated
-#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
-#define PRINTF_SUPPORT_PTRDIFF_T
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-// internal flag definitions
-#define FLAGS_ZEROPAD (1U << 0U)
-#define FLAGS_LEFT (1U << 1U)
-#define FLAGS_PLUS (1U << 2U)
-#define FLAGS_SPACE (1U << 3U)
-#define FLAGS_HASH (1U << 4U)
-#define FLAGS_UPPERCASE (1U << 5U)
-#define FLAGS_CHAR (1U << 6U)
-#define FLAGS_SHORT (1U << 7U)
-#define FLAGS_LONG (1U << 8U)
-#define FLAGS_LONG_LONG (1U << 9U)
-#define FLAGS_PRECISION (1U << 10U)
-#define FLAGS_ADAPT_EXP (1U << 11U)
-
-
-// import float.h for DBL_MAX
-#if defined(PRINTF_SUPPORT_FLOAT)
-#include <float.h>
-#endif
-
-
-// output function type
-typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
-
-
-// wrapper (used as buffer) for output function type
-typedef struct {
- void (*fct)(char character, void* arg);
- void* arg;
-} out_fct_wrap_type;
-
-
-// internal buffer output
-static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
-{
- if (idx < maxlen) {
- ((char*)buffer)[idx] = character;
- }
-}
-
-
-// internal null output
-static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
-{
- (void)character; (void)buffer; (void)idx; (void)maxlen;
-}
-
-
-// internal _putchar wrapper
-static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
-{
- (void)buffer; (void)idx; (void)maxlen;
- if (character) {
- _putchar(character);
- }
-}
-
-
-// internal output function wrapper
-static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
-{
- (void)idx; (void)maxlen;
- if (character) {
- // buffer is the output fct pointer
- ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
- }
-}
-
-
-// internal secure strlen
-// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
-static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
-{
- const char* s;
- for (s = str; *s && maxsize--; ++s);
- return (unsigned int)(s - str);
-}
-
-
-// internal test if char is a digit (0-9)
-// \return true if char is a digit
-static inline bool _is_digit(char ch)
-{
- return (ch >= '0') && (ch <= '9');
-}
-
-
-// internal ASCII string to unsigned int conversion
-static unsigned int _atoi(const char** str)
-{
- unsigned int i = 0U;
- while (_is_digit(**str)) {
- i = i * 10U + (unsigned int)(*((*str)++) - '0');
- }
- return i;
-}
-
-
-// output the specified string in reverse, taking care of any zero-padding
-static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags)
-{
- const size_t start_idx = idx;
-
- // pad spaces up to given width
- if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
- for (size_t i = len; i < width; i++) {
- out(' ', buffer, idx++, maxlen);
- }
- }
-
- // reverse string
- while (len) {
- out(buf[--len], buffer, idx++, maxlen);
- }
-
- // append pad spaces up to given width
- if (flags & FLAGS_LEFT) {
- while (idx - start_idx < width) {
- out(' ', buffer, idx++, maxlen);
- }
- }
-
- return idx;
-}
-
-
-// internal itoa format
-static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
-{
- // pad leading zeros
- if (!(flags & FLAGS_LEFT)) {
- if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
- width--;
- }
- while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
- buf[len++] = '0';
- }
- while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
- buf[len++] = '0';
- }
- }
-
- // handle hash
- if (flags & FLAGS_HASH) {
- if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
- len--;
- if (len && (base == 16U)) {
- len--;
- }
- }
- if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
- buf[len++] = 'x';
- }
- else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
- buf[len++] = 'X';
- }
- else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
- buf[len++] = 'b';
- }
- if (len < PRINTF_NTOA_BUFFER_SIZE) {
- buf[len++] = '0';
- }
- }
-
- if (len < PRINTF_NTOA_BUFFER_SIZE) {
- if (negative) {
- buf[len++] = '-';
- }
- else if (flags & FLAGS_PLUS) {
- buf[len++] = '+'; // ignore the space if the '+' exists
- }
- else if (flags & FLAGS_SPACE) {
- buf[len++] = ' ';
- }
- }
-
- return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
-}
-
-
-// internal itoa for 'long' type
-static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
-{
- char buf[PRINTF_NTOA_BUFFER_SIZE];
- size_t len = 0U;
-
- // no hash for 0 values
- if (!value) {
- flags &= ~FLAGS_HASH;
- }
-
- // write if precision != 0 and value is != 0
- if (!(flags & FLAGS_PRECISION) || value) {
- do {
- const char digit = (char)(value % base);
- buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
- value /= base;
- } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
- }
-
- return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
-}
-
-
-// internal itoa for 'long long' type
-#if defined(PRINTF_SUPPORT_LONG_LONG)
-static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
-{
- char buf[PRINTF_NTOA_BUFFER_SIZE];
- size_t len = 0U;
-
- // no hash for 0 values
- if (!value) {
- flags &= ~FLAGS_HASH;
- }
-
- // write if precision != 0 and value is != 0
- if (!(flags & FLAGS_PRECISION) || value) {
- do {
- const char digit = (char)(value % base);
- buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
- value /= base;
- } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
- }
-
- return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
-}
-#endif // PRINTF_SUPPORT_LONG_LONG
-
-
-#if defined(PRINTF_SUPPORT_FLOAT)
-
-#if defined(PRINTF_SUPPORT_EXPONENTIAL)
-// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
-static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
-#endif
-
-
-// internal ftoa for fixed decimal floating point
-static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
-{
- char buf[PRINTF_FTOA_BUFFER_SIZE];
- size_t len = 0U;
- double diff = 0.0;
-
- // powers of 10
- static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
-
- // test for special values
- if (value != value)
- return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
- if (value < -DBL_MAX)
- return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
- if (value > DBL_MAX)
- return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
-
- // test for very large values
- // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
- if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
-#if defined(PRINTF_SUPPORT_EXPONENTIAL)
- return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
-#else
- return 0U;
-#endif
- }
-
- // test for negative
- bool negative = false;
- if (value < 0) {
- negative = true;
- value = 0 - value;
- }
-
- // set default precision, if not set explicitly
- if (!(flags & FLAGS_PRECISION)) {
- prec = PRINTF_DEFAULT_FLOAT_PRECISION;
- }
- // limit precision to 9, cause a prec >= 10 can lead to overflow errors
- while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
- buf[len++] = '0';
- prec--;
- }
-
- int whole = (int)value;
- double tmp = (value - whole) * pow10[prec];
- unsigned long frac = (unsigned long)tmp;
- diff = tmp - frac;
-
- if (diff > 0.5) {
- ++frac;
- // handle rollover, e.g. case 0.99 with prec 1 is 1.0
- if (frac >= pow10[prec]) {
- frac = 0;
- ++whole;
- }
- }
- else if (diff < 0.5) {
- }
- else if ((frac == 0U) || (frac & 1U)) {
- // if halfway, round up if odd OR if last digit is 0
- ++frac;
- }
-
- if (prec == 0U) {
- diff = value - (double)whole;
- if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
- // exactly 0.5 and ODD, then round up
- // 1.5 -> 2, but 2.5 -> 2
- ++whole;
- }
- }
- else {
- unsigned int count = prec;
- // now do fractional part, as an unsigned number
- while (len < PRINTF_FTOA_BUFFER_SIZE) {
- --count;
- buf[len++] = (char)(48U + (frac % 10U));
- if (!(frac /= 10U)) {
- break;
- }
- }
- // add extra 0s
- while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
- buf[len++] = '0';
- }
- if (len < PRINTF_FTOA_BUFFER_SIZE) {
- // add decimal
- buf[len++] = '.';
- }
- }
-
- // do whole part, number is reversed
- while (len < PRINTF_FTOA_BUFFER_SIZE) {
- buf[len++] = (char)(48 + (whole % 10));
- if (!(whole /= 10)) {
- break;
- }
- }
-
- // pad leading zeros
- if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
- if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
- width--;
- }
- while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
- buf[len++] = '0';
- }
- }
-
- if (len < PRINTF_FTOA_BUFFER_SIZE) {
- if (negative) {
- buf[len++] = '-';
- }
- else if (flags & FLAGS_PLUS) {
- buf[len++] = '+'; // ignore the space if the '+' exists
- }
- else if (flags & FLAGS_SPACE) {
- buf[len++] = ' ';
- }
- }
-
- return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
-}
-
-
-#if defined(PRINTF_SUPPORT_EXPONENTIAL)
-// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
-static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
-{
- // check for NaN and special values
- if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
- return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
- }
-
- // determine the sign
- const bool negative = value < 0;
- if (negative) {
- value = -value;
- }
-
- // default precision
- if (!(flags & FLAGS_PRECISION)) {
- prec = PRINTF_DEFAULT_FLOAT_PRECISION;
- }
-
- // determine the decimal exponent
- // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
- union {
- uint64_t U;
- double F;
- } conv;
-
- conv.F = value;
- int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
- conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
- // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
- int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
- // now we want to compute 10^expval but we want to be sure it won't overflow
- exp2 = (int)(expval * 3.321928094887362 + 0.5);
- const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
- const double z2 = z * z;
- conv.U = (uint64_t)(exp2 + 1023) << 52U;
- // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
- conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
- // correct for rounding errors
- if (value < conv.F) {
- expval--;
- conv.F /= 10;
- }
-
- // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
- unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
-
- // in "%g" mode, "prec" is the number of *significant figures* not decimals
- if (flags & FLAGS_ADAPT_EXP) {
- // do we want to fall-back to "%f" mode?
- if ((value >= 1e-4) && (value < 1e6)) {
- if ((int)prec > expval) {
- prec = (unsigned)((int)prec - expval - 1);
- }
- else {
- prec = 0;
- }
- flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
- // no characters in exponent
- minwidth = 0U;
- expval = 0;
- }
- else {
- // we use one sigfig for the whole part
- if ((prec > 0) && (flags & FLAGS_PRECISION)) {
- --prec;
- }
- }
- }
-
- // will everything fit?
- unsigned int fwidth = width;
- if (width > minwidth) {
- // we didn't fall-back so subtract the characters required for the exponent
- fwidth -= minwidth;
- } else {
- // not enough characters, so go back to default sizing
- fwidth = 0U;
- }
- if ((flags & FLAGS_LEFT) && minwidth) {
- // if we're padding on the right, DON'T pad the floating part
- fwidth = 0U;
- }
-
- // rescale the float value
- if (expval) {
- value /= conv.F;
- }
-
- // output the floating part
- const size_t start_idx = idx;
- idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
-
- // output the exponent part
- if (minwidth) {
- // output the exponential symbol
- out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
- // output the exponent value
- idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
- // might need to right-pad spaces
- if (flags & FLAGS_LEFT) {
- while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
- }
- }
- return idx;
-}
-#endif // PRINTF_SUPPORT_EXPONENTIAL
-#endif // PRINTF_SUPPORT_FLOAT
-
-
-// internal vsnprintf
-static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
-{
- unsigned int flags, width, precision, n;
- size_t idx = 0U;
-
- if (!buffer) {
- // use null output function
- out = _out_null;
- }
-
- while (*format)
- {
- // format specifier? %[flags][width][.precision][length]
- if (*format != '%') {
- // no
- out(*format, buffer, idx++, maxlen);
- format++;
- continue;
- }
- else {
- // yes, evaluate it
- format++;
- }
-
- // evaluate flags
- flags = 0U;
- do {
- switch (*format) {
- case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
- case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
- case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
- case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
- case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
- default : n = 0U; break;
- }
- } while (n);
-
- // evaluate width field
- width = 0U;
- if (_is_digit(*format)) {
- width = _atoi(&format);
- }
- else if (*format == '*') {
- const int w = va_arg(va, int);
- if (w < 0) {
- flags |= FLAGS_LEFT; // reverse padding
- width = (unsigned int)-w;
- }
- else {
- width = (unsigned int)w;
- }
- format++;
- }
-
- // evaluate precision field
- precision = 0U;
- if (*format == '.') {
- flags |= FLAGS_PRECISION;
- format++;
- if (_is_digit(*format)) {
- precision = _atoi(&format);
- }
- else if (*format == '*') {
- const int prec = (int)va_arg(va, int);
- precision = prec > 0 ? (unsigned int)prec : 0U;
- format++;
- }
- }
-
- // evaluate length field
- switch (*format) {
- case 'l' :
- flags |= FLAGS_LONG;
- format++;
- if (*format == 'l') {
- flags |= FLAGS_LONG_LONG;
- format++;
- }
- break;
- case 'h' :
- flags |= FLAGS_SHORT;
- format++;
- if (*format == 'h') {
- flags |= FLAGS_CHAR;
- format++;
- }
- break;
-#if defined(PRINTF_SUPPORT_PTRDIFF_T)
- case 't' :
- flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
- format++;
- break;
-#endif
- case 'j' :
- flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
- format++;
- break;
- case 'z' :
- flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
- format++;
- break;
- default :
- break;
- }
-
- // evaluate specifier
- switch (*format) {
- case 'd' :
- case 'i' :
- case 'u' :
- case 'x' :
- case 'X' :
- case 'o' :
- case 'b' : {
- // set the base
- unsigned int base;
- if (*format == 'x' || *format == 'X') {
- base = 16U;
- }
- else if (*format == 'o') {
- base = 8U;
- }
- else if (*format == 'b') {
- base = 2U;
- }
- else {
- base = 10U;
- flags &= ~FLAGS_HASH; // no hash for dec format
- }
- // uppercase
- if (*format == 'X') {
- flags |= FLAGS_UPPERCASE;
- }
-
- // no plus or space flag for u, x, X, o, b
- if ((*format != 'i') && (*format != 'd')) {
- flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
- }
-
- // ignore '0' flag when precision is given
- if (flags & FLAGS_PRECISION) {
- flags &= ~FLAGS_ZEROPAD;
- }
-
- // convert the integer
- if ((*format == 'i') || (*format == 'd')) {
- // signed
- if (flags & FLAGS_LONG_LONG) {
-#if defined(PRINTF_SUPPORT_LONG_LONG)
- const long long value = va_arg(va, long long);
- idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
-#endif
- }
- else if (flags & FLAGS_LONG) {
- const long value = va_arg(va, long);
- idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
- }
- else {
- const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
- idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
- }
- }
- else {
- // unsigned
- if (flags & FLAGS_LONG_LONG) {
-#if defined(PRINTF_SUPPORT_LONG_LONG)
- idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
-#endif
- }
- else if (flags & FLAGS_LONG) {
- idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
- }
- else {
- const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
- idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
- }
- }
- format++;
- break;
- }
-#if defined(PRINTF_SUPPORT_FLOAT)
- case 'f' :
- case 'F' :
- if (*format == 'F') flags |= FLAGS_UPPERCASE;
- idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
- format++;
- break;
-#if defined(PRINTF_SUPPORT_EXPONENTIAL)
- case 'e':
- case 'E':
- case 'g':
- case 'G':
- if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
- if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
- idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
- format++;
- break;
-#endif // PRINTF_SUPPORT_EXPONENTIAL
-#endif // PRINTF_SUPPORT_FLOAT
- case 'c' : {
- unsigned int l = 1U;
- // pre padding
- if (!(flags & FLAGS_LEFT)) {
- while (l++ < width) {
- out(' ', buffer, idx++, maxlen);
- }
- }
- // char output
- out((char)va_arg(va, int), buffer, idx++, maxlen);
- // post padding
- if (flags & FLAGS_LEFT) {
- while (l++ < width) {
- out(' ', buffer, idx++, maxlen);
- }
- }
- format++;
- break;
- }
-
- case 's' : {
- const char* p = va_arg(va, char*);
- unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
- // pre padding
- if (flags & FLAGS_PRECISION) {
- l = (l < precision ? l : precision);
- }
- if (!(flags & FLAGS_LEFT)) {
- while (l++ < width) {
- out(' ', buffer, idx++, maxlen);
- }
- }
- // string output
- while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
- out(*(p++), buffer, idx++, maxlen);
- }
- // post padding
- if (flags & FLAGS_LEFT) {
- while (l++ < width) {
- out(' ', buffer, idx++, maxlen);
- }
- }
- format++;
- break;
- }
-
- case 'p' : {
- width = sizeof(void*) * 2U;
- flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
-#if defined(PRINTF_SUPPORT_LONG_LONG)
- const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
- if (is_ll) {
- idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
- }
- else {
-#endif
- idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
-#if defined(PRINTF_SUPPORT_LONG_LONG)
- }
-#endif
- format++;
- break;
- }
-
- case '%' :
- out('%', buffer, idx++, maxlen);
- format++;
- break;
-
- default :
- out(*format, buffer, idx++, maxlen);
- format++;
- break;
- }
- }
-
- // termination
- out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
-
- // return written chars without terminating \0
- return (int)idx;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-int printf_(const char* format, ...)
-{
- va_list va;
- va_start(va, format);
- char buffer[1];
- const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
- va_end(va);
- return ret;
-}
-
-
-int sprintf_(char* buffer, const char* format, ...)
-{
- va_list va;
- va_start(va, format);
- const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
- va_end(va);
- return ret;
-}
-
-
-int snprintf_(char* buffer, size_t count, const char* format, ...)
-{
- va_list va;
- va_start(va, format);
- const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
- va_end(va);
- return ret;
-}
-
-
-int vprintf_(const char* format, va_list va)
-{
- char buffer[1];
- return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
-}
-
-
-int vsnprintf_(char* buffer, size_t count, const char* format, va_list va)
-{
- return _vsnprintf(_out_buffer, buffer, count, format, va);
-}
-
-
-int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
-{
- va_list va;
- va_start(va, format);
- const out_fct_wrap_type out_fct_wrap = { out, arg };
- const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
- va_end(va);
- return ret;
-}
diff --git a/na/lib/printf.h b/na/lib/printf.h
deleted file mode 100644
index 8aa23cf..0000000
--- a/na/lib/printf.h
+++ /dev/null
@@ -1,129 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// \author (c) Marco Paland (info@paland.com)
-// 2014-2019, PALANDesign Hannover, Germany
-//
-// \license The MIT License (MIT)
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
-// embedded systems with a very limited resources.
-// Use this instead of bloated standard/newlib printf.
-// These routines are thread safe and reentrant.
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef _PRINTF_H_
-#define _PRINTF_H_
-
-#include <stdarg.h>
-
-/* LumeOS: avoid stddef.h — define types from compiler builtins directly.
- * This works in both C and C++ and matches our types.h definition. */
-#ifndef _SIZE_T_DEFINED
-#define _SIZE_T_DEFINED
-typedef __SIZE_TYPE__ size_t;
-typedef __PTRDIFF_TYPE__ ptrdiff_t;
-typedef __INTMAX_TYPE__ intmax_t;
-#endif
-
-/* LumeOS: kernel does not need floating-point formatted output */
-#define PRINTF_DISABLE_SUPPORT_FLOAT
-#define PRINTF_DISABLE_SUPPORT_EXPONENTIAL
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/**
- * Output a character to a custom device like UART, used by the printf() function
- * This function is declared here only. You have to write your custom implementation somewhere
- * \param character Character to output
- */
-void _putchar(char character);
-
-
-/**
- * Tiny printf implementation
- * You have to implement _putchar if you use printf()
- * To avoid conflicts with the regular printf() API it is overridden by macro defines
- * and internal underscore-appended functions like printf_() are used
- * \param format A string that specifies the format of the output
- * \return The number of characters that are written into the array, not counting the terminating null character
- */
-#define printf printf_
-int printf_(const char* format, ...);
-
-
-/**
- * Tiny sprintf implementation
- * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
- * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
- * \param format A string that specifies the format of the output
- * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
- */
-#define sprintf sprintf_
-int sprintf_(char* buffer, const char* format, ...);
-
-
-/**
- * Tiny snprintf/vsnprintf implementation
- * \param buffer A pointer to the buffer where to store the formatted string
- * \param count The maximum number of characters to store in the buffer, including a terminating null character
- * \param format A string that specifies the format of the output
- * \param va A value identifying a variable arguments list
- * \return The number of characters that COULD have been written into the buffer, not counting the terminating
- * null character. A value equal or larger than count indicates truncation. Only when the returned value
- * is non-negative and less than count, the string has been completely written.
- */
-#define snprintf snprintf_
-#define vsnprintf vsnprintf_
-int snprintf_(char* buffer, size_t count, const char* format, ...);
-int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
-
-
-/**
- * Tiny vprintf implementation
- * \param format A string that specifies the format of the output
- * \param va A value identifying a variable arguments list
- * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
- */
-#define vprintf vprintf_
-int vprintf_(const char* format, va_list va);
-
-
-/**
- * printf with output function
- * You may use this as dynamic alternative to printf() with its fixed _putchar() output
- * \param out An output function which takes one character and an argument pointer
- * \param arg An argument pointer for user data passed to output function
- * \param format A string that specifies the format of the output
- * \return The number of characters that are sent to the output function, not counting the terminating null character
- */
-int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif // _PRINTF_H_
diff --git a/na/lib/random.cc b/na/lib/random.cc
deleted file mode 100644
index 428c75c..0000000
--- a/na/lib/random.cc
+++ /dev/null
@@ -1,152 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <lume/random.h>
-#include <arch/random.h>
-#include <lume/klog.h>
-
-#include <lume/kprintf.h>
-
-/*
- * Early Boot PRNG (xoshiro256**)
- * State size: 256 bits (4 * 64-bit words)
- * Extremely fast, excellent statistical properties.
- */
-
-static uint64 g_state[4];
-static bool g_initialized = false;
-static uint64 g_init_seed = 0;
-static RandomSeedSource g_seed_source = RandomSeedSource::Unknown;
-
-static inline uint64 rotl(const uint64 x, int k) {
- return (x << k) | (x >> (64 - k));
-}
-
-/* SplitMix64 used to initialize xoshiro state from a single 64-bit seed */
-static uint64 splitmix64(uint64& state) {
- uint64 z = (state += 0x9e3779b97f4a7c15ULL);
- z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
- z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
- return z ^ (z >> 31);
-}
-
-void random_early_init(uint64 seed)
-{
- if (g_initialized) return;
-
- RandomSeedSource source = RandomSeedSource::Unknown;
-
- /* If seed is 0 (not provided by bootloader), try hardware */
- if (seed == 0) {
- uint64 hw_val;
- if (arch::get_hw_random(hw_val)) {
- seed = hw_val;
- source = RandomSeedSource::HwRandom;
- } else {
- /* Fallback: use boot cycles mixed with a constant */
- seed = arch::get_cycles() ^ 0xdeadbeefbadc0ffeULL;
- source = RandomSeedSource::CycleFallback;
- }
- } else {
- source = RandomSeedSource::FdtProvided;
- }
-
- uint64 sm_state = seed;
- g_state[0] = splitmix64(sm_state);
- g_state[1] = splitmix64(sm_state);
- g_state[2] = splitmix64(sm_state);
- g_state[3] = splitmix64(sm_state);
-
- g_init_seed = seed;
- g_seed_source = source;
- g_initialized = true;
- kprintf("[random] early PRNG initialized, seed=0x%llx, source=%s\n",
- seed, random_seed_source_name(source));
-}
-
-uint64 get_random_u64()
-{
- if (!g_initialized) {
- random_early_init(0);
- }
-
- const uint64 result = rotl(g_state[1] * 5, 7) * 9;
-
- const uint64 t = g_state[1] << 17;
-
- g_state[2] ^= g_state[0];
- g_state[3] ^= g_state[1];
- g_state[1] ^= g_state[2];
- g_state[0] ^= g_state[3];
-
- g_state[2] ^= t;
- g_state[3] = rotl(g_state[3], 45);
-
- /* Mix with current cycles for slight additional unpredictability */
- return result ^ arch::get_cycles();
-}
-
-uint32 get_random_u32()
-{
- return static_cast<uint32>(get_random_u64() >> 32);
-}
-
-void get_random_bytes(void* buf, size_t len)
-{
- uint8* p = static_cast<uint8*>(buf);
- while (len >= 8) {
- uint64 val = get_random_u64();
- for (int i = 0; i < 8; i++) {
- p[i] = (val >> (i * 8)) & 0xFF;
- }
- p += 8;
- len -= 8;
- }
- if (len > 0) {
- uint64 val = get_random_u64();
- for (size_t i = 0; i < len; i++) {
- p[i] = (val >> (i * 8)) & 0xFF;
- }
- }
-}
-
-void add_device_randomness(const void* buf, size_t len)
-{
- /*
- * For now, just mix into the xoshiro state using a simple XOR.
- * In the future, this will feed the CSPRNG entropy pool.
- */
- const uint8* p = static_cast<const uint8*>(buf);
- uint64 mix = arch::get_cycles();
- for (size_t i = 0; i < len; i++) {
- mix = (mix << 5) ^ (mix >> 59) ^ p[i];
- }
-
- g_state[0] ^= mix;
- g_state[1] ^= arch::get_cycles();
-}
-
-RandomSeedSource random_early_seed_source()
-{
- return g_seed_source;
-}
-
-uint64 random_early_seed_value()
-{
- return g_init_seed;
-}
-
-const char* random_seed_source_name(RandomSeedSource source)
-{
- switch (source) {
- case RandomSeedSource::FdtProvided:
- return "fdt";
- case RandomSeedSource::HwRandom:
- return "hw";
- case RandomSeedSource::CycleFallback:
- return "cycle";
- case RandomSeedSource::Unknown:
- default:
- return "unknown";
- }
-}
diff --git a/na/lib/rbtree.cc b/na/lib/rbtree.cc
deleted file mode 100644
index 6718a1a..0000000
--- a/na/lib/rbtree.cc
+++ /dev/null
@@ -1,596 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- Red Black Trees
- (C) 1999 Andrea Arcangeli <andrea@suse.de>
- (C) 2002 David Woodhouse <dwmw2@infradead.org>
- (C) 2012 Michel Lespinasse <walken@google.com>
-
-
- linux/lib/rbtree.c
-*/
-
-#include <lume/rbtree_augmented.h>
-
-
-/*
- * red-black trees properties: https://en.wikipedia.org/wiki/Rbtree
- *
- * 1) A node is either red or black
- * 2) The root is black
- * 3) All leaves (nullptr) are black
- * 4) Both children of every red node are black
- * 5) Every simple path from root to leaves contains the same number
- * of black nodes.
- *
- * 4 and 5 give the O(log n) guarantee, since 4 implies you cannot have two
- * consecutive red nodes in a path and every red node is therefore followed by
- * a black. So if B is the number of black nodes on every simple path (as per
- * 5), then the longest possible path due to 4 is 2B.
- *
- * We shall indicate color with case, where black nodes are uppercase and red
- * nodes will be lowercase. Unknown color nodes shall be drawn as red within
- * parentheses and have some accompanying text comment.
- */
-
-/*
- * Notes on lockless lookups:
- *
- * All stores to the tree structure (rb_left and rb_right) must be done using
- * WRITE_ONCE(). And we must not inadvertently cause (temporary) loops in the
- * tree structure as seen in program order.
- *
- * These two requirements will allow lockless iteration of the tree -- not
- * correct iteration mind you, tree rotations are not atomic so a lookup might
- * miss entire subtrees.
- *
- * But they do guarantee that any such traversal will only see valid elements
- * and that it will indeed complete -- does not get stuck in a loop.
- *
- * It also guarantees that if the lookup returns an element it is the 'correct'
- * one. But not returning an element does _NOT_ mean it's not present.
- *
- * NOTE:
- *
- * Stores to __rb_parent_color are not important for simple lookups so those
- * are left undone as of now. Nor did I check for loops involving parent
- * pointers.
- */
-
-static inline void rb_set_black(struct rb_node *rb)
-{
- rb->__rb_parent_color += RB_BLACK;
-}
-
-static inline struct rb_node *rb_red_parent(struct rb_node *red)
-{
- return (struct rb_node *)red->__rb_parent_color;
-}
-
-/*
- * Helper function for rotations:
- * - old's parent and color get assigned to _new
- * - old gets assigned _new as a parent and 'color' as a color.
- */
-static inline void
-__rb_rotate_set_parents(struct rb_node *old, struct rb_node *_new,
- struct rb_root *root, int color)
-{
- struct rb_node *parent = rb_parent(old);
- _new->__rb_parent_color = old->__rb_parent_color;
- rb_set_parent_color(old, _new, color);
- __rb_change_child(old, _new, parent, root);
-}
-
-static __always_inline void
-__rb_insert(struct rb_node *node, struct rb_root *root,
- void (*augment_rotate)(struct rb_node *old, struct rb_node *_new))
-{
- struct rb_node *parent = rb_red_parent(node), *gparent, *tmp;
-
- while (true) {
- /*
- * Loop invariant: node is red.
- */
- if (unlikely(!parent)) {
- /*
- * The inserted node is root. Either this is the
- * first node, or we recursed at Case 1 below and
- * are no longer violating 4).
- */
- rb_set_parent_color(node, nullptr, RB_BLACK);
- break;
- }
-
- /*
- * If there is a black parent, we are done.
- * Otherwise, take some corrective action as,
- * per 4), we don't want a red root or two
- * consecutive red nodes.
- */
- if(rb_is_black(parent))
- break;
-
- gparent = rb_red_parent(parent);
-
- tmp = gparent->rb_right;
- if (parent != tmp) { /* parent == gparent->rb_left */
- if (tmp && rb_is_red(tmp)) {
- /*
- * Case 1 - node's uncle is red (color flips).
- *
- * G g
- * / \ / \
- * p u --> P U
- * / /
- * n n
- *
- * However, since g's parent might be red, and
- * 4) does not allow this, we need to recurse
- * at g.
- */
- rb_set_parent_color(tmp, gparent, RB_BLACK);
- rb_set_parent_color(parent, gparent, RB_BLACK);
- node = gparent;
- parent = rb_parent(node);
- rb_set_parent_color(node, parent, RB_RED);
- continue;
- }
-
- tmp = parent->rb_right;
- if (node == tmp) {
- /*
- * Case 2 - node's uncle is black and node is
- * the parent's right child (left rotate at parent).
- *
- * G G
- * / \ / \
- * p U --> n U
- * \ /
- * n p
- *
- * This still leaves us in violation of 4), the
- * continuation into Case 3 will fix that.
- */
- tmp = node->rb_left;
- WRITE_ONCE(parent->rb_right, tmp);
- WRITE_ONCE(node->rb_left, parent);
- if (tmp)
- rb_set_parent_color(tmp, parent,
- RB_BLACK);
- rb_set_parent_color(parent, node, RB_RED);
- augment_rotate(parent, node);
- parent = node;
- tmp = node->rb_right;
- }
-
- /*
- * Case 3 - node's uncle is black and node is
- * the parent's left child (right rotate at gparent).
- *
- * G P
- * / \ / \
- * p U --> n g
- * / \
- * n U
- */
- WRITE_ONCE(gparent->rb_left, tmp); /* == parent->rb_right */
- WRITE_ONCE(parent->rb_right, gparent);
- if (tmp)
- rb_set_parent_color(tmp, gparent, RB_BLACK);
- __rb_rotate_set_parents(gparent, parent, root, RB_RED);
- augment_rotate(gparent, parent);
- break;
- } else {
- tmp = gparent->rb_left;
- if (tmp && rb_is_red(tmp)) {
- /* Case 1 - color flips */
- rb_set_parent_color(tmp, gparent, RB_BLACK);
- rb_set_parent_color(parent, gparent, RB_BLACK);
- node = gparent;
- parent = rb_parent(node);
- rb_set_parent_color(node, parent, RB_RED);
- continue;
- }
-
- tmp = parent->rb_left;
- if (node == tmp) {
- /* Case 2 - right rotate at parent */
- tmp = node->rb_right;
- WRITE_ONCE(parent->rb_left, tmp);
- WRITE_ONCE(node->rb_right, parent);
- if (tmp)
- rb_set_parent_color(tmp, parent,
- RB_BLACK);
- rb_set_parent_color(parent, node, RB_RED);
- augment_rotate(parent, node);
- parent = node;
- tmp = node->rb_left;
- }
-
- /* Case 3 - left rotate at gparent */
- WRITE_ONCE(gparent->rb_right, tmp); /* == parent->rb_left */
- WRITE_ONCE(parent->rb_left, gparent);
- if (tmp)
- rb_set_parent_color(tmp, gparent, RB_BLACK);
- __rb_rotate_set_parents(gparent, parent, root, RB_RED);
- augment_rotate(gparent, parent);
- break;
- }
- }
-}
-
-/*
- * Inline version for rb_erase() use - we want to be able to inline
- * and eliminate the dummy_rotate callback there
- */
-static __always_inline void
-____rb_erase_color(struct rb_node *parent, struct rb_root *root,
- void (*augment_rotate)(struct rb_node *old, struct rb_node *_new))
-{
- struct rb_node *node = nullptr, *sibling, *tmp1, *tmp2;
-
- while (true) {
- /*
- * Loop invariants:
- * - node is black (or nullptr on first iteration)
- * - node is not the root (parent is not nullptr)
- * - All leaf paths going through parent and node have a
- * black node count that is 1 lower than other leaf paths.
- */
- sibling = parent->rb_right;
- if (node != sibling) { /* node == parent->rb_left */
- if (rb_is_red(sibling)) {
- /*
- * Case 1 - left rotate at parent
- *
- * P S
- * / \ / \
- * N s --> p Sr
- * / \ / \
- * Sl Sr N Sl
- */
- tmp1 = sibling->rb_left;
- WRITE_ONCE(parent->rb_right, tmp1);
- WRITE_ONCE(sibling->rb_left, parent);
- rb_set_parent_color(tmp1, parent, RB_BLACK);
- __rb_rotate_set_parents(parent, sibling, root,
- RB_RED);
- augment_rotate(parent, sibling);
- sibling = tmp1;
- }
- tmp1 = sibling->rb_right;
- if (!tmp1 || rb_is_black(tmp1)) {
- tmp2 = sibling->rb_left;
- if (!tmp2 || rb_is_black(tmp2)) {
- /*
- * Case 2 - sibling color flip
- * (p could be either color here)
- *
- * (p) (p)
- * / \ / \
- * N S --> N s
- * / \ / \
- * Sl Sr Sl Sr
- *
- * This leaves us violating 5) which
- * can be fixed by flipping p to black
- * if it was red, or by recursing at p.
- * p is red when coming from Case 1.
- */
- rb_set_parent_color(sibling, parent,
- RB_RED);
- if (rb_is_red(parent))
- rb_set_black(parent);
- else {
- node = parent;
- parent = rb_parent(node);
- if (parent)
- continue;
- }
- break;
- }
- /*
- * Case 3 - right rotate at sibling
- * (p could be either color here)
- *
- * (p) (p)
- * / \ / \
- * N S --> N sl
- * / \ \
- * sl Sr S
- * \
- * Sr
- *
- * Note: p might be red, and then both
- * p and sl are red after rotation(which
- * breaks property 4). This is fixed in
- * Case 4 (in __rb_rotate_set_parents()
- * which set sl the color of p
- * and set p RB_BLACK)
- *
- * (p) (sl)
- * / \ / \
- * N sl --> P S
- * \ / \
- * S N Sr
- * \
- * Sr
- */
- tmp1 = tmp2->rb_right;
- WRITE_ONCE(sibling->rb_left, tmp1);
- WRITE_ONCE(tmp2->rb_right, sibling);
- WRITE_ONCE(parent->rb_right, tmp2);
- if (tmp1)
- rb_set_parent_color(tmp1, sibling,
- RB_BLACK);
- augment_rotate(sibling, tmp2);
- tmp1 = sibling;
- sibling = tmp2;
- }
- /*
- * Case 4 - left rotate at parent + color flips
- * (p and sl could be either color here.
- * After rotation, p becomes black, s acquires
- * p's color, and sl keeps its color)
- *
- * (p) (s)
- * / \ / \
- * N S --> P Sr
- * / \ / \
- * (sl) sr N (sl)
- */
- tmp2 = sibling->rb_left;
- WRITE_ONCE(parent->rb_right, tmp2);
- WRITE_ONCE(sibling->rb_left, parent);
- rb_set_parent_color(tmp1, sibling, RB_BLACK);
- if (tmp2)
- rb_set_parent(tmp2, parent);
- __rb_rotate_set_parents(parent, sibling, root,
- RB_BLACK);
- augment_rotate(parent, sibling);
- break;
- } else {
- sibling = parent->rb_left;
- if (rb_is_red(sibling)) {
- /* Case 1 - right rotate at parent */
- tmp1 = sibling->rb_right;
- WRITE_ONCE(parent->rb_left, tmp1);
- WRITE_ONCE(sibling->rb_right, parent);
- rb_set_parent_color(tmp1, parent, RB_BLACK);
- __rb_rotate_set_parents(parent, sibling, root,
- RB_RED);
- augment_rotate(parent, sibling);
- sibling = tmp1;
- }
- tmp1 = sibling->rb_left;
- if (!tmp1 || rb_is_black(tmp1)) {
- tmp2 = sibling->rb_right;
- if (!tmp2 || rb_is_black(tmp2)) {
- /* Case 2 - sibling color flip */
- rb_set_parent_color(sibling, parent,
- RB_RED);
- if (rb_is_red(parent))
- rb_set_black(parent);
- else {
- node = parent;
- parent = rb_parent(node);
- if (parent)
- continue;
- }
- break;
- }
- /* Case 3 - left rotate at sibling */
- tmp1 = tmp2->rb_left;
- WRITE_ONCE(sibling->rb_right, tmp1);
- WRITE_ONCE(tmp2->rb_left, sibling);
- WRITE_ONCE(parent->rb_left, tmp2);
- if (tmp1)
- rb_set_parent_color(tmp1, sibling,
- RB_BLACK);
- augment_rotate(sibling, tmp2);
- tmp1 = sibling;
- sibling = tmp2;
- }
- /* Case 4 - right rotate at parent + color flips */
- tmp2 = sibling->rb_right;
- WRITE_ONCE(parent->rb_left, tmp2);
- WRITE_ONCE(sibling->rb_right, parent);
- rb_set_parent_color(tmp1, sibling, RB_BLACK);
- if (tmp2)
- rb_set_parent(tmp2, parent);
- __rb_rotate_set_parents(parent, sibling, root,
- RB_BLACK);
- augment_rotate(parent, sibling);
- break;
- }
- }
-}
-
-/* Non-inline version for rb_erase_augmented() use */
-void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
- void (*augment_rotate)(struct rb_node *old, struct rb_node *_new))
-{
- ____rb_erase_color(parent, root, augment_rotate);
-}
-
-/*
- * Non-augmented rbtree manipulation functions.
- *
- * We use dummy augmented callbacks here, and have the compiler optimize them
- * out of the rb_insert_color() and rb_erase() function definitions.
- */
-
-static inline void dummy_propagate(struct rb_node *node, struct rb_node *stop) {}
-static inline void dummy_copy(struct rb_node *old, struct rb_node *_new) {}
-static inline void dummy_rotate(struct rb_node *old, struct rb_node *_new) {}
-
-static const struct rb_augment_callbacks dummy_callbacks = {
- .propagate = dummy_propagate,
- .copy = dummy_copy,
- .rotate = dummy_rotate
-};
-
-void rb_insert_color(struct rb_node *node, struct rb_root *root)
-{
- __rb_insert(node, root, dummy_rotate);
-}
-
-void rb_erase(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *rebalance;
- rebalance = __rb_erase_augmented(node, root, &dummy_callbacks);
- if (rebalance)
- ____rb_erase_color(rebalance, root, dummy_rotate);
-}
-
-
-/*
- * Augmented rbtree manipulation functions.
- *
- * This instantiates the same __always_inline functions as in the non-augmented
- * case, but this time with user-defined callbacks.
- */
-
-void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,
- void (*augment_rotate)(struct rb_node *old, struct rb_node *_new))
-{
- __rb_insert(node, root, augment_rotate);
-}
-
-struct rb_node *rb_next(const struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (RB_EMPTY_NODE(node))
- return nullptr;
-
- /*
- * If we have a right-hand child, go down and then left as far
- * as we can.
- */
- if (node->rb_right) {
- node = node->rb_right;
- while (node->rb_left)
- node = node->rb_left;
- return (struct rb_node *)node;
- }
-
- /*
- * No right-hand children. Everything down and left is smaller than us,
- * so any 'next' node must be in the general direction of our parent.
- * Go up the tree; any time the ancestor is a right-hand child of its
- * parent, keep going up. First time it's a left-hand child of its
- * parent, said parent is our 'next' node.
- */
- while ((parent = rb_parent(node)) && node == parent->rb_right)
- node = parent;
-
- return parent;
-}
-
-struct rb_node *rb_prev(const struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (RB_EMPTY_NODE(node))
- return nullptr;
-
- /*
- * If we have a left-hand child, go down and then right as far
- * as we can.
- */
- if (node->rb_left) {
- node = node->rb_left;
- while (node->rb_right)
- node = node->rb_right;
- return (struct rb_node *)node;
- }
-
- /*
- * No left-hand children. Go up till we find an ancestor which
- * is a right-hand child of its parent.
- */
- while ((parent = rb_parent(node)) && node == parent->rb_left)
- node = parent;
-
- return parent;
-}
-
-void rb_replace_node(struct rb_node *victim, struct rb_node *_new,
- struct rb_root *root)
-{
- struct rb_node *parent = rb_parent(victim);
-
- /* Copy the pointers/colour from the victim to the replacement */
- *_new = *victim;
-
- /* Set the surrounding nodes to point to the replacement */
- if (victim->rb_left)
- rb_set_parent(victim->rb_left, _new);
- if (victim->rb_right)
- rb_set_parent(victim->rb_right, _new);
- __rb_change_child(victim, _new, parent, root);
-}
-
-
-static struct rb_node *rb_left_deepest_node(const struct rb_node *node)
-{
- for (;;) {
- if (node->rb_left)
- node = node->rb_left;
- else if (node->rb_right)
- node = node->rb_right;
- else
- return (struct rb_node *)node;
- }
-}
-
-struct rb_node *rb_next_postorder(const struct rb_node *node)
-{
- const struct rb_node *parent;
- if (!node)
- return nullptr;
- parent = rb_parent(node);
-
- /* If we're sitting on node, we've already seen our children */
- if (parent && node == parent->rb_left && parent->rb_right) {
- /* If we are the parent's left node, go to the parent's right
- * node then all the way down to the left */
- return rb_left_deepest_node(parent->rb_right);
- } else
- /* Otherwise we are the parent's right node, and the parent
- * should be next */
- return (struct rb_node *)parent;
-}
-
-struct rb_node *rb_first_postorder(const struct rb_root *root)
-{
- if (!root->rb_node)
- return nullptr;
-
- return rb_left_deepest_node(root->rb_node);
-}
-
-struct rb_node *rb_first(const struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return nullptr;
- while (n->rb_left)
- n = n->rb_left;
- return n;
-}
-
-struct rb_node *rb_last(const struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return nullptr;
- while (n->rb_right)
- n = n->rb_right;
- return n;
-}
diff --git a/na/lib/string/COPYRIGHT b/na/lib/string/COPYRIGHT
deleted file mode 100644
index 2f15edc..0000000
--- a/na/lib/string/COPYRIGHT
+++ /dev/null
@@ -1,193 +0,0 @@
-musl as a whole is licensed under the following standard MIT license:
-
-----------------------------------------------------------------------
-Copyright © 2005-2020 Rich Felker, et al.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-----------------------------------------------------------------------
-
-Authors/contributors include:
-
-A. Wilcox
-Ada Worcester
-Alex Dowad
-Alex Suykov
-Alexander Monakov
-Andre McCurdy
-Andrew Kelley
-Anthony G. Basile
-Aric Belsito
-Arvid Picciani
-Bartosz Brachaczek
-Benjamin Peterson
-Bobby Bingham
-Boris Brezillon
-Brent Cook
-Chris Spiegel
-Clément Vasseur
-Daniel Micay
-Daniel Sabogal
-Daurnimator
-David Carlier
-David Edelsohn
-Denys Vlasenko
-Dmitry Ivanov
-Dmitry V. Levin
-Drew DeVault
-Emil Renner Berthing
-Fangrui Song
-Felix Fietkau
-Felix Janda
-Gianluca Anzolin
-Hauke Mehrtens
-He X
-Hiltjo Posthuma
-Isaac Dunham
-Jaydeep Patil
-Jens Gustedt
-Jeremy Huntwork
-Jo-Philipp Wich
-Joakim Sindholt
-John Spencer
-Julien Ramseier
-Justin Cormack
-Kaarle Ritvanen
-Khem Raj
-Kylie McClain
-Leah Neukirchen
-Luca Barbato
-Luka Perkov
-Lynn Ochs
-M Farkas-Dyck (Strake)
-Mahesh Bodapati
-Markus Wichmann
-Masanori Ogino
-Michael Clark
-Michael Forney
-Mikhail Kremnyov
-Natanael Copa
-Nicholas J. Kain
-orc
-Pascal Cuoq
-Patrick Oppenlander
-Petr Hosek
-Petr Skocik
-Pierre Carrier
-Reini Urban
-Rich Felker
-Richard Pennington
-Ryan Fairfax
-Samuel Holland
-Segev Finer
-Shiz
-sin
-Solar Designer
-Stefan Kristiansson
-Stefan O'Rear
-Szabolcs Nagy
-Timo Teräs
-Trutz Behn
-Will Dietz
-William Haddon
-William Pitcock
-
-Portions of this software are derived from third-party works licensed
-under terms compatible with the above MIT license:
-
-The TRE regular expression implementation (src/regex/reg* and
-src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed
-under a 2-clause BSD license (license text in the source files). The
-included version has been heavily modified by Rich Felker in 2012, in
-the interests of size, simplicity, and namespace cleanliness.
-
-Much of the math library code (src/math/* and src/complex/*) is
-Copyright © 1993,2004 Sun Microsystems or
-Copyright © 2003-2011 David Schultz or
-Copyright © 2003-2009 Steven G. Kargl or
-Copyright © 2003-2009 Bruce D. Evans or
-Copyright © 2008 Stephen L. Moshier or
-Copyright © 2017-2018 Arm Limited
-and labelled as such in comments in the individual source files. All
-have been licensed under extremely permissive terms.
-
-The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008
-The Android Open Source Project and is licensed under a two-clause BSD
-license. It was taken from Bionic libc, used on Android.
-
-The AArch64 memcpy and memset code (src/string/aarch64/*) are
-Copyright © 1999-2019, Arm Limited.
-
-The implementation of DES for crypt (src/crypt/crypt_des.c) is
-Copyright © 1994 David Burren. It is licensed under a BSD license.
-
-The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was
-originally written by Solar Designer and placed into the public
-domain. The code also comes with a fallback permissive license for use
-in jurisdictions that may not recognize the public domain.
-
-The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
-Lynn Ochs and is licensed under an MIT-style license.
-
-The x86_64 port was written by Nicholas J. Kain and is licensed under
-the standard MIT terms.
-
-The mips and microblaze ports were originally written by Richard
-Pennington for use in the ellcc project. The original code was adapted
-by Rich Felker for build system and code conventions during upstream
-integration. It is licensed under the standard MIT terms.
-
-The mips64 port was contributed by Imagination Technologies and is
-licensed under the standard MIT terms.
-
-The powerpc port was also originally written by Richard Pennington,
-and later supplemented and integrated by John Spencer. It is licensed
-under the standard MIT terms.
-
-All other files which have no copyright comments are original works
-produced specifically for use as part of this library, written either
-by Rich Felker, the main author of the library, or by one or more
-contibutors listed above. Details on authorship of individual files
-can be found in the git version control history of the project. The
-omission of copyright and license comments in each file is in the
-interest of source tree size.
-
-In addition, permission is hereby granted for all public header files
-(include/* and arch/*/bits/*) and crt files intended to be linked into
-applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit
-the copyright notice and permission notice otherwise required by the
-license, and to use these files without any requirement of
-attribution. These files include substantial contributions from:
-
-Bobby Bingham
-John Spencer
-Nicholas J. Kain
-Rich Felker
-Richard Pennington
-Stefan Kristiansson
-Szabolcs Nagy
-
-all of whom have explicitly granted such permission.
-
-This file previously contained text expressing a belief that most of
-the files covered by the above exception were sufficiently trivial not
-to be subject to copyright, resulting in confusion over whether it
-negated the permissions granted in the license. In the spirit of
-permissive licensing, and of not having licensing issues being an
-obstacle to adoption, that text has been removed.
diff --git a/na/lib/string/aarch64/memcpy.S b/na/lib/string/aarch64/memcpy.S
deleted file mode 100644
index 48bb8a8..0000000
--- a/na/lib/string/aarch64/memcpy.S
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * memcpy - copy memory area
- *
- * Copyright (c) 2012-2020, Arm Limited.
- * SPDX-License-Identifier: MIT
- */
-
-/* Assumptions:
- *
- * ARMv8-a, AArch64, unaligned accesses.
- *
- */
-
-#define dstin x0
-#define src x1
-#define count x2
-#define dst x3
-#define srcend x4
-#define dstend x5
-#define A_l x6
-#define A_lw w6
-#define A_h x7
-#define B_l x8
-#define B_lw w8
-#define B_h x9
-#define C_l x10
-#define C_lw w10
-#define C_h x11
-#define D_l x12
-#define D_h x13
-#define E_l x14
-#define E_h x15
-#define F_l x16
-#define F_h x17
-#define G_l count
-#define G_h dst
-#define H_l src
-#define H_h srcend
-#define tmp1 x14
-
-/* This implementation of memcpy uses unaligned accesses and branchless
- sequences to keep the code small, simple and improve performance.
-
- Copies are split into 3 main cases: small copies of up to 32 bytes, medium
- copies of up to 128 bytes, and large copies. The overhead of the overlap
- check is negligible since it is only required for large copies.
-
- Large copies use a software pipelined loop processing 64 bytes per iteration.
- The destination pointer is 16-byte aligned to minimize unaligned accesses.
- The loop tail is handled by always copying 64 bytes from the end.
-*/
-
-.global memcpy
-.type memcpy,%function
-memcpy:
- add srcend, src, count
- add dstend, dstin, count
- cmp count, 128
- b.hi .Lcopy_long
- cmp count, 32
- b.hi .Lcopy32_128
-
- /* Small copies: 0..32 bytes. */
- cmp count, 16
- b.lo .Lcopy16
- ldp A_l, A_h, [src]
- ldp D_l, D_h, [srcend, -16]
- stp A_l, A_h, [dstin]
- stp D_l, D_h, [dstend, -16]
- ret
-
- /* Copy 8-15 bytes. */
-.Lcopy16:
- tbz count, 3, .Lcopy8
- ldr A_l, [src]
- ldr A_h, [srcend, -8]
- str A_l, [dstin]
- str A_h, [dstend, -8]
- ret
-
- .p2align 3
- /* Copy 4-7 bytes. */
-.Lcopy8:
- tbz count, 2, .Lcopy4
- ldr A_lw, [src]
- ldr B_lw, [srcend, -4]
- str A_lw, [dstin]
- str B_lw, [dstend, -4]
- ret
-
- /* Copy 0..3 bytes using a branchless sequence. */
-.Lcopy4:
- cbz count, .Lcopy0
- lsr tmp1, count, 1
- ldrb A_lw, [src]
- ldrb C_lw, [srcend, -1]
- ldrb B_lw, [src, tmp1]
- strb A_lw, [dstin]
- strb B_lw, [dstin, tmp1]
- strb C_lw, [dstend, -1]
-.Lcopy0:
- ret
-
- .p2align 4
- /* Medium copies: 33..128 bytes. */
-.Lcopy32_128:
- ldp A_l, A_h, [src]
- ldp B_l, B_h, [src, 16]
- ldp C_l, C_h, [srcend, -32]
- ldp D_l, D_h, [srcend, -16]
- cmp count, 64
- b.hi .Lcopy128
- stp A_l, A_h, [dstin]
- stp B_l, B_h, [dstin, 16]
- stp C_l, C_h, [dstend, -32]
- stp D_l, D_h, [dstend, -16]
- ret
-
- .p2align 4
- /* Copy 65..128 bytes. */
-.Lcopy128:
- ldp E_l, E_h, [src, 32]
- ldp F_l, F_h, [src, 48]
- cmp count, 96
- b.ls .Lcopy96
- ldp G_l, G_h, [srcend, -64]
- ldp H_l, H_h, [srcend, -48]
- stp G_l, G_h, [dstend, -64]
- stp H_l, H_h, [dstend, -48]
-.Lcopy96:
- stp A_l, A_h, [dstin]
- stp B_l, B_h, [dstin, 16]
- stp E_l, E_h, [dstin, 32]
- stp F_l, F_h, [dstin, 48]
- stp C_l, C_h, [dstend, -32]
- stp D_l, D_h, [dstend, -16]
- ret
-
- .p2align 4
- /* Copy more than 128 bytes. */
-.Lcopy_long:
-
- /* Copy 16 bytes and then align dst to 16-byte alignment. */
-
- ldp D_l, D_h, [src]
- and tmp1, dstin, 15
- bic dst, dstin, 15
- sub src, src, tmp1
- add count, count, tmp1 /* Count is now 16 too large. */
- ldp A_l, A_h, [src, 16]
- stp D_l, D_h, [dstin]
- ldp B_l, B_h, [src, 32]
- ldp C_l, C_h, [src, 48]
- ldp D_l, D_h, [src, 64]!
- subs count, count, 128 + 16 /* Test and readjust count. */
- b.ls .Lcopy64_from_end
-
-.Lloop64:
- stp A_l, A_h, [dst, 16]
- ldp A_l, A_h, [src, 16]
- stp B_l, B_h, [dst, 32]
- ldp B_l, B_h, [src, 32]
- stp C_l, C_h, [dst, 48]
- ldp C_l, C_h, [src, 48]
- stp D_l, D_h, [dst, 64]!
- ldp D_l, D_h, [src, 64]!
- subs count, count, 64
- b.hi .Lloop64
-
- /* Write the last iteration and copy 64 bytes from the end. */
-.Lcopy64_from_end:
- ldp E_l, E_h, [srcend, -64]
- stp A_l, A_h, [dst, 16]
- ldp A_l, A_h, [srcend, -48]
- stp B_l, B_h, [dst, 32]
- ldp B_l, B_h, [srcend, -32]
- stp C_l, C_h, [dst, 48]
- ldp C_l, C_h, [srcend, -16]
- stp D_l, D_h, [dst, 64]
- stp E_l, E_h, [dstend, -64]
- stp A_l, A_h, [dstend, -48]
- stp B_l, B_h, [dstend, -32]
- stp C_l, C_h, [dstend, -16]
- ret
-
-.size memcpy,.-memcpy
diff --git a/na/lib/string/aarch64/memset.S b/na/lib/string/aarch64/memset.S
deleted file mode 100644
index f0d29b7..0000000
--- a/na/lib/string/aarch64/memset.S
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * memset - fill memory with a constant byte
- *
- * Copyright (c) 2012-2020, Arm Limited.
- * SPDX-License-Identifier: MIT
- */
-
-/* Assumptions:
- *
- * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses.
- *
- */
-
-#define dstin x0
-#define val x1
-#define valw w1
-#define count x2
-#define dst x3
-#define dstend x4
-#define zva_val x5
-
-.global memset
-.type memset,%function
-memset:
-
- dup v0.16B, valw
- add dstend, dstin, count
-
- cmp count, 96
- b.hi .Lset_long
- cmp count, 16
- b.hs .Lset_medium
- mov val, v0.D[0]
-
- /* Set 0..15 bytes. */
- tbz count, 3, 1f
- str val, [dstin]
- str val, [dstend, -8]
- ret
- nop
-1: tbz count, 2, 2f
- str valw, [dstin]
- str valw, [dstend, -4]
- ret
-2: cbz count, 3f
- strb valw, [dstin]
- tbz count, 1, 3f
- strh valw, [dstend, -2]
-3: ret
-
- /* Set 17..96 bytes. */
-.Lset_medium:
- str q0, [dstin]
- tbnz count, 6, .Lset96
- str q0, [dstend, -16]
- tbz count, 5, 1f
- str q0, [dstin, 16]
- str q0, [dstend, -32]
-1: ret
-
- .p2align 4
- /* Set 64..96 bytes. Write 64 bytes from the start and
- 32 bytes from the end. */
-.Lset96:
- str q0, [dstin, 16]
- stp q0, q0, [dstin, 32]
- stp q0, q0, [dstend, -32]
- ret
-
- .p2align 4
-.Lset_long:
- and valw, valw, 255
- bic dst, dstin, 15
- str q0, [dstin]
- cmp count, 160
- ccmp valw, 0, 0, hs
- b.ne .Lno_zva
-
-#ifndef SKIP_ZVA_CHECK
- mrs zva_val, dczid_el0
- and zva_val, zva_val, 31
- cmp zva_val, 4 /* ZVA size is 64 bytes. */
- b.ne .Lno_zva
-#endif
- str q0, [dst, 16]
- stp q0, q0, [dst, 32]
- bic dst, dst, 63
- sub count, dstend, dst /* Count is now 64 too large. */
- sub count, count, 128 /* Adjust count and bias for loop. */
-
- .p2align 4
-.Lzva_loop:
- add dst, dst, 64
- dc zva, dst
- subs count, count, 64
- b.hi .Lzva_loop
- stp q0, q0, [dstend, -64]
- stp q0, q0, [dstend, -32]
- ret
-
-.Lno_zva:
- sub count, dstend, dst /* Count is 16 too large. */
- sub dst, dst, 16 /* Dst is biased by -32. */
- sub count, count, 64 + 16 /* Adjust count and bias for loop. */
-.Lno_zva_loop:
- stp q0, q0, [dst, 32]
- stp q0, q0, [dst, 64]!
- subs count, count, 64
- b.hi .Lno_zva_loop
- stp q0, q0, [dstend, -64]
- stp q0, q0, [dstend, -32]
- ret
-
-.size memset,.-memset
-
diff --git a/na/lib/string/arm/__aeabi_memcpy.s b/na/lib/string/arm/__aeabi_memcpy.s
deleted file mode 100644
index 3a527e4..0000000
--- a/na/lib/string/arm/__aeabi_memcpy.s
+++ /dev/null
@@ -1,45 +0,0 @@
-.syntax unified
-
-.global __aeabi_memcpy8
-.global __aeabi_memcpy4
-.global __aeabi_memcpy
-.global __aeabi_memmove8
-.global __aeabi_memmove4
-.global __aeabi_memmove
-
-.type __aeabi_memcpy8,%function
-.type __aeabi_memcpy4,%function
-.type __aeabi_memcpy,%function
-.type __aeabi_memmove8,%function
-.type __aeabi_memmove4,%function
-.type __aeabi_memmove,%function
-
-__aeabi_memmove8:
-__aeabi_memmove4:
-__aeabi_memmove:
- cmp r0, r1
- bls 3f
- cmp r2, #0
- beq 2f
- adds r0, r0, r2
- adds r2, r1, r2
-1: subs r2, r2, #1
- ldrb r3, [r2]
- subs r0, r0, #1
- strb r3, [r0]
- cmp r1, r2
- bne 1b
-2: bx lr
-__aeabi_memcpy8:
-__aeabi_memcpy4:
-__aeabi_memcpy:
-3: cmp r2, #0
- beq 2f
- adds r2, r1, r2
-1: ldrb r3, [r1]
- adds r1, r1, #1
- strb r3, [r0]
- adds r0, r0, #1
- cmp r1, r2
- bne 1b
-2: bx lr
diff --git a/na/lib/string/arm/__aeabi_memset.s b/na/lib/string/arm/__aeabi_memset.s
deleted file mode 100644
index f9f6058..0000000
--- a/na/lib/string/arm/__aeabi_memset.s
+++ /dev/null
@@ -1,31 +0,0 @@
-.syntax unified
-
-.global __aeabi_memclr8
-.global __aeabi_memclr4
-.global __aeabi_memclr
-.global __aeabi_memset8
-.global __aeabi_memset4
-.global __aeabi_memset
-
-.type __aeabi_memclr8,%function
-.type __aeabi_memclr4,%function
-.type __aeabi_memclr,%function
-.type __aeabi_memset8,%function
-.type __aeabi_memset4,%function
-.type __aeabi_memset,%function
-
-__aeabi_memclr8:
-__aeabi_memclr4:
-__aeabi_memclr:
- movs r2, #0
-__aeabi_memset8:
-__aeabi_memset4:
-__aeabi_memset:
- cmp r1, #0
- beq 2f
- adds r1, r0, r1
-1: strb r2, [r0]
- adds r0, r0, #1
- cmp r1, r0
- bne 1b
-2: bx lr
diff --git a/na/lib/string/arm/memcpy.S b/na/lib/string/arm/memcpy.S
deleted file mode 100644
index 869e344..0000000
--- a/na/lib/string/arm/memcpy.S
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-
-/*
- * Optimized memcpy() for ARM.
- *
- * note that memcpy() always returns the destination pointer,
- * so we have to preserve R0.
- */
-
-/*
- * This file has been modified from the original for use in musl libc.
- * The main changes are: addition of .type memcpy,%function to make the
- * code safely callable from thumb mode, adjusting the return
- * instructions to be compatible with pre-thumb ARM cpus, removal of
- * prefetch code that is not compatible with older cpus and support for
- * building as thumb 2 and big-endian.
- */
-
-.syntax unified
-
-.global memcpy
-.type memcpy,%function
-memcpy:
- /* The stack must always be 64-bits aligned to be compliant with the
- * ARM ABI. Since we have to save R0, we might as well save R4
- * which we can use for better pipelining of the reads below
- */
- .fnstart
- .save {r0, r4, lr}
- stmfd sp!, {r0, r4, lr}
- /* Making room for r5-r11 which will be spilled later */
- .pad #28
- sub sp, sp, #28
-
- /* it simplifies things to take care of len<4 early */
- cmp r2, #4
- blo copy_last_3_and_return
-
- /* compute the offset to align the source
- * offset = (4-(src&3))&3 = -src & 3
- */
- rsb r3, r1, #0
- ands r3, r3, #3
- beq src_aligned
-
- /* align source to 32 bits. We need to insert 2 instructions between
- * a ldr[b|h] and str[b|h] because byte and half-word instructions
- * stall 2 cycles.
- */
- movs r12, r3, lsl #31
- sub r2, r2, r3 /* we know that r3 <= r2 because r2 >= 4 */
- ldrbmi r3, [r1], #1
- ldrbcs r4, [r1], #1
- ldrbcs r12,[r1], #1
- strbmi r3, [r0], #1
- strbcs r4, [r0], #1
- strbcs r12,[r0], #1
-
-src_aligned:
-
- /* see if src and dst are aligned together (congruent) */
- eor r12, r0, r1
- tst r12, #3
- bne non_congruent
-
- /* Use post-incriment mode for stm to spill r5-r11 to reserved stack
- * frame. Don't update sp.
- */
- stmea sp, {r5-r11}
-
- /* align the destination to a cache-line */
- rsb r3, r0, #0
- ands r3, r3, #0x1C
- beq congruent_aligned32
- cmp r3, r2
- andhi r3, r2, #0x1C
-
- /* conditionnaly copies 0 to 7 words (length in r3) */
- movs r12, r3, lsl #28
- ldmcs r1!, {r4, r5, r6, r7} /* 16 bytes */
- ldmmi r1!, {r8, r9} /* 8 bytes */
- stmcs r0!, {r4, r5, r6, r7}
- stmmi r0!, {r8, r9}
- tst r3, #0x4
- ldrne r10,[r1], #4 /* 4 bytes */
- strne r10,[r0], #4
- sub r2, r2, r3
-
-congruent_aligned32:
- /*
- * here source is aligned to 32 bytes.
- */
-
-cached_aligned32:
- subs r2, r2, #32
- blo less_than_32_left
-
- /*
- * We preload a cache-line up to 64 bytes ahead. On the 926, this will
- * stall only until the requested world is fetched, but the linefill
- * continues in the the background.
- * While the linefill is going, we write our previous cache-line
- * into the write-buffer (which should have some free space).
- * When the linefill is done, the writebuffer will
- * start dumping its content into memory
- *
- * While all this is going, we then load a full cache line into
- * 8 registers, this cache line should be in the cache by now
- * (or partly in the cache).
- *
- * This code should work well regardless of the source/dest alignment.
- *
- */
-
- /* Align the preload register to a cache-line because the cpu does
- * "critical word first" (the first word requested is loaded first).
- */
- @ bic r12, r1, #0x1F
- @ add r12, r12, #64
-
-1: ldmia r1!, { r4-r11 }
- subs r2, r2, #32
-
- /*
- * NOTE: if r12 is more than 64 ahead of r1, the following ldrhi
- * for ARM9 preload will not be safely guarded by the preceding subs.
- * When it is safely guarded the only possibility to have SIGSEGV here
- * is because the caller overstates the length.
- */
- @ ldrhi r3, [r12], #32 /* cheap ARM9 preload */
- stmia r0!, { r4-r11 }
- bhs 1b
-
- add r2, r2, #32
-
-less_than_32_left:
- /*
- * less than 32 bytes left at this point (length in r2)
- */
-
- /* skip all this if there is nothing to do, which should
- * be a common case (if not executed the code below takes
- * about 16 cycles)
- */
- tst r2, #0x1F
- beq 1f
-
- /* conditionnaly copies 0 to 31 bytes */
- movs r12, r2, lsl #28
- ldmcs r1!, {r4, r5, r6, r7} /* 16 bytes */
- ldmmi r1!, {r8, r9} /* 8 bytes */
- stmcs r0!, {r4, r5, r6, r7}
- stmmi r0!, {r8, r9}
- movs r12, r2, lsl #30
- ldrcs r3, [r1], #4 /* 4 bytes */
- ldrhmi r4, [r1], #2 /* 2 bytes */
- strcs r3, [r0], #4
- strhmi r4, [r0], #2
- tst r2, #0x1
- ldrbne r3, [r1] /* last byte */
- strbne r3, [r0]
-
- /* we're done! restore everything and return */
-1: ldmfd sp!, {r5-r11}
- ldmfd sp!, {r0, r4, lr}
- bx lr
-
- /********************************************************************/
-
-non_congruent:
- /*
- * here source is aligned to 4 bytes
- * but destination is not.
- *
- * in the code below r2 is the number of bytes read
- * (the number of bytes written is always smaller, because we have
- * partial words in the shift queue)
- */
- cmp r2, #4
- blo copy_last_3_and_return
-
- /* Use post-incriment mode for stm to spill r5-r11 to reserved stack
- * frame. Don't update sp.
- */
- stmea sp, {r5-r11}
-
- /* compute shifts needed to align src to dest */
- rsb r5, r0, #0
- and r5, r5, #3 /* r5 = # bytes in partial words */
- mov r12, r5, lsl #3 /* r12 = right */
- rsb lr, r12, #32 /* lr = left */
-
- /* read the first word */
- ldr r3, [r1], #4
- sub r2, r2, #4
-
- /* write a partial word (0 to 3 bytes), such that destination
- * becomes aligned to 32 bits (r5 = nb of words to copy for alignment)
- */
- movs r5, r5, lsl #31
-
-#if __ARMEB__
- movmi r3, r3, ror #24
- strbmi r3, [r0], #1
- movcs r3, r3, ror #24
- strbcs r3, [r0], #1
- movcs r3, r3, ror #24
- strbcs r3, [r0], #1
-#else
- strbmi r3, [r0], #1
- movmi r3, r3, lsr #8
- strbcs r3, [r0], #1
- movcs r3, r3, lsr #8
- strbcs r3, [r0], #1
- movcs r3, r3, lsr #8
-#endif
-
- cmp r2, #4
- blo partial_word_tail
-
-#if __ARMEB__
- mov r3, r3, lsr r12
- mov r3, r3, lsl r12
-#endif
-
- /* Align destination to 32 bytes (cache line boundary) */
-1: tst r0, #0x1c
- beq 2f
- ldr r5, [r1], #4
- sub r2, r2, #4
-#if __ARMEB__
- mov r4, r5, lsr lr
- orr r4, r4, r3
- mov r3, r5, lsl r12
-#else
- mov r4, r5, lsl lr
- orr r4, r4, r3
- mov r3, r5, lsr r12
-#endif
- str r4, [r0], #4
- cmp r2, #4
- bhs 1b
- blo partial_word_tail
-
- /* copy 32 bytes at a time */
-2: subs r2, r2, #32
- blo less_than_thirtytwo
-
- /* Use immediate mode for the shifts, because there is an extra cycle
- * for register shifts, which could account for up to 50% of
- * performance hit.
- */
-
- cmp r12, #24
- beq loop24
- cmp r12, #8
- beq loop8
-
-loop16:
- ldr r12, [r1], #4
-1: mov r4, r12
- ldmia r1!, { r5,r6,r7, r8,r9,r10,r11}
- subs r2, r2, #32
- ldrhs r12, [r1], #4
-#if __ARMEB__
- orr r3, r3, r4, lsr #16
- mov r4, r4, lsl #16
- orr r4, r4, r5, lsr #16
- mov r5, r5, lsl #16
- orr r5, r5, r6, lsr #16
- mov r6, r6, lsl #16
- orr r6, r6, r7, lsr #16
- mov r7, r7, lsl #16
- orr r7, r7, r8, lsr #16
- mov r8, r8, lsl #16
- orr r8, r8, r9, lsr #16
- mov r9, r9, lsl #16
- orr r9, r9, r10, lsr #16
- mov r10, r10, lsl #16
- orr r10, r10, r11, lsr #16
- stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
- mov r3, r11, lsl #16
-#else
- orr r3, r3, r4, lsl #16
- mov r4, r4, lsr #16
- orr r4, r4, r5, lsl #16
- mov r5, r5, lsr #16
- orr r5, r5, r6, lsl #16
- mov r6, r6, lsr #16
- orr r6, r6, r7, lsl #16
- mov r7, r7, lsr #16
- orr r7, r7, r8, lsl #16
- mov r8, r8, lsr #16
- orr r8, r8, r9, lsl #16
- mov r9, r9, lsr #16
- orr r9, r9, r10, lsl #16
- mov r10, r10, lsr #16
- orr r10, r10, r11, lsl #16
- stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
- mov r3, r11, lsr #16
-#endif
- bhs 1b
- b less_than_thirtytwo
-
-loop8:
- ldr r12, [r1], #4
-1: mov r4, r12
- ldmia r1!, { r5,r6,r7, r8,r9,r10,r11}
- subs r2, r2, #32
- ldrhs r12, [r1], #4
-#if __ARMEB__
- orr r3, r3, r4, lsr #24
- mov r4, r4, lsl #8
- orr r4, r4, r5, lsr #24
- mov r5, r5, lsl #8
- orr r5, r5, r6, lsr #24
- mov r6, r6, lsl #8
- orr r6, r6, r7, lsr #24
- mov r7, r7, lsl #8
- orr r7, r7, r8, lsr #24
- mov r8, r8, lsl #8
- orr r8, r8, r9, lsr #24
- mov r9, r9, lsl #8
- orr r9, r9, r10, lsr #24
- mov r10, r10, lsl #8
- orr r10, r10, r11, lsr #24
- stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
- mov r3, r11, lsl #8
-#else
- orr r3, r3, r4, lsl #24
- mov r4, r4, lsr #8
- orr r4, r4, r5, lsl #24
- mov r5, r5, lsr #8
- orr r5, r5, r6, lsl #24
- mov r6, r6, lsr #8
- orr r6, r6, r7, lsl #24
- mov r7, r7, lsr #8
- orr r7, r7, r8, lsl #24
- mov r8, r8, lsr #8
- orr r8, r8, r9, lsl #24
- mov r9, r9, lsr #8
- orr r9, r9, r10, lsl #24
- mov r10, r10, lsr #8
- orr r10, r10, r11, lsl #24
- stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
- mov r3, r11, lsr #8
-#endif
- bhs 1b
- b less_than_thirtytwo
-
-loop24:
- ldr r12, [r1], #4
-1: mov r4, r12
- ldmia r1!, { r5,r6,r7, r8,r9,r10,r11}
- subs r2, r2, #32
- ldrhs r12, [r1], #4
-#if __ARMEB__
- orr r3, r3, r4, lsr #8
- mov r4, r4, lsl #24
- orr r4, r4, r5, lsr #8
- mov r5, r5, lsl #24
- orr r5, r5, r6, lsr #8
- mov r6, r6, lsl #24
- orr r6, r6, r7, lsr #8
- mov r7, r7, lsl #24
- orr r7, r7, r8, lsr #8
- mov r8, r8, lsl #24
- orr r8, r8, r9, lsr #8
- mov r9, r9, lsl #24
- orr r9, r9, r10, lsr #8
- mov r10, r10, lsl #24
- orr r10, r10, r11, lsr #8
- stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
- mov r3, r11, lsl #24
-#else
- orr r3, r3, r4, lsl #8
- mov r4, r4, lsr #24
- orr r4, r4, r5, lsl #8
- mov r5, r5, lsr #24
- orr r5, r5, r6, lsl #8
- mov r6, r6, lsr #24
- orr r6, r6, r7, lsl #8
- mov r7, r7, lsr #24
- orr r7, r7, r8, lsl #8
- mov r8, r8, lsr #24
- orr r8, r8, r9, lsl #8
- mov r9, r9, lsr #24
- orr r9, r9, r10, lsl #8
- mov r10, r10, lsr #24
- orr r10, r10, r11, lsl #8
- stmia r0!, {r3,r4,r5,r6, r7,r8,r9,r10}
- mov r3, r11, lsr #24
-#endif
- bhs 1b
-
-less_than_thirtytwo:
- /* copy the last 0 to 31 bytes of the source */
- rsb r12, lr, #32 /* we corrupted r12, recompute it */
- add r2, r2, #32
- cmp r2, #4
- blo partial_word_tail
-
-1: ldr r5, [r1], #4
- sub r2, r2, #4
-#if __ARMEB__
- mov r4, r5, lsr lr
- orr r4, r4, r3
- mov r3, r5, lsl r12
-#else
- mov r4, r5, lsl lr
- orr r4, r4, r3
- mov r3, r5, lsr r12
-#endif
- str r4, [r0], #4
- cmp r2, #4
- bhs 1b
-
-partial_word_tail:
- /* we have a partial word in the input buffer */
- movs r5, lr, lsl #(31-3)
-#if __ARMEB__
- movmi r3, r3, ror #24
- strbmi r3, [r0], #1
- movcs r3, r3, ror #24
- strbcs r3, [r0], #1
- movcs r3, r3, ror #24
- strbcs r3, [r0], #1
-#else
- strbmi r3, [r0], #1
- movmi r3, r3, lsr #8
- strbcs r3, [r0], #1
- movcs r3, r3, lsr #8
- strbcs r3, [r0], #1
-#endif
-
- /* Refill spilled registers from the stack. Don't update sp. */
- ldmfd sp, {r5-r11}
-
-copy_last_3_and_return:
- movs r2, r2, lsl #31 /* copy remaining 0, 1, 2 or 3 bytes */
- ldrbmi r2, [r1], #1
- ldrbcs r3, [r1], #1
- ldrbcs r12,[r1]
- strbmi r2, [r0], #1
- strbcs r3, [r0], #1
- strbcs r12,[r0]
-
- /* we're done! restore sp and spilled registers and return */
- add sp, sp, #28
- ldmfd sp!, {r0, r4, lr}
- bx lr
-
diff --git a/na/lib/string/bcmp.c b/na/lib/string/bcmp.c
deleted file mode 100644
index d4c093f..0000000
--- a/na/lib/string/bcmp.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _BSD_SOURCE
-#include <string.h>
-#include <strings.h>
-
-int bcmp(const void *s1, const void *s2, size_t n)
-{
- return memcmp(s1, s2, n);
-}
diff --git a/na/lib/string/bcopy.c b/na/lib/string/bcopy.c
deleted file mode 100644
index 25e98a2..0000000
--- a/na/lib/string/bcopy.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _BSD_SOURCE
-#include <string.h>
-#include <strings.h>
-
-void bcopy(const void *s1, void *s2, size_t n)
-{
- memmove(s2, s1, n);
-}
diff --git a/na/lib/string/bzero.c b/na/lib/string/bzero.c
deleted file mode 100644
index 08f1398..0000000
--- a/na/lib/string/bzero.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _BSD_SOURCE
-#include <string.h>
-#include <strings.h>
-
-void bzero(void *s, size_t n)
-{
- memset(s, 0, n);
-}
diff --git a/na/lib/string/explicit_bzero.c b/na/lib/string/explicit_bzero.c
deleted file mode 100644
index c94f25f..0000000
--- a/na/lib/string/explicit_bzero.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _BSD_SOURCE
-#include <string.h>
-
-void explicit_bzero(void *d, size_t n)
-{
- d = memset(d, 0, n);
- __asm__ __volatile__ ("" : : "r"(d) : "memory");
-}
diff --git a/na/lib/string/i386/memcpy.s b/na/lib/string/i386/memcpy.s
deleted file mode 100644
index 0608dd8..0000000
--- a/na/lib/string/i386/memcpy.s
+++ /dev/null
@@ -1,32 +0,0 @@
-.global memcpy
-.global __memcpy_fwd
-.hidden __memcpy_fwd
-.type memcpy,@function
-memcpy:
-__memcpy_fwd:
- push %esi
- push %edi
- mov 12(%esp),%edi
- mov 16(%esp),%esi
- mov 20(%esp),%ecx
- mov %edi,%eax
- cmp $4,%ecx
- jc 1f
- test $3,%edi
- jz 1f
-2: movsb
- dec %ecx
- test $3,%edi
- jnz 2b
-1: mov %ecx,%edx
- shr $2,%ecx
- rep
- movsl
- and $3,%edx
- jz 1f
-2: movsb
- dec %edx
- jnz 2b
-1: pop %edi
- pop %esi
- ret
diff --git a/na/lib/string/i386/memmove.s b/na/lib/string/i386/memmove.s
deleted file mode 100644
index 2a6a504..0000000
--- a/na/lib/string/i386/memmove.s
+++ /dev/null
@@ -1,22 +0,0 @@
-.global memmove
-.type memmove,@function
-memmove:
- mov 4(%esp),%eax
- sub 8(%esp),%eax
- cmp 12(%esp),%eax
-.hidden __memcpy_fwd
- jae __memcpy_fwd
- push %esi
- push %edi
- mov 12(%esp),%edi
- mov 16(%esp),%esi
- mov 20(%esp),%ecx
- lea -1(%edi,%ecx),%edi
- lea -1(%esi,%ecx),%esi
- std
- rep movsb
- cld
- lea 1(%edi),%eax
- pop %edi
- pop %esi
- ret
diff --git a/na/lib/string/i386/memset.s b/na/lib/string/i386/memset.s
deleted file mode 100644
index d00422c..0000000
--- a/na/lib/string/i386/memset.s
+++ /dev/null
@@ -1,76 +0,0 @@
-.global memset
-.type memset,@function
-memset:
- mov 12(%esp),%ecx
- cmp $62,%ecx
- ja 2f
-
- mov 8(%esp),%dl
- mov 4(%esp),%eax
- test %ecx,%ecx
- jz 1f
-
- mov %dl,%dh
-
- mov %dl,(%eax)
- mov %dl,-1(%eax,%ecx)
- cmp $2,%ecx
- jbe 1f
-
- mov %dx,1(%eax)
- mov %dx,(-1-2)(%eax,%ecx)
- cmp $6,%ecx
- jbe 1f
-
- shl $16,%edx
- mov 8(%esp),%dl
- mov 8(%esp),%dh
-
- mov %edx,(1+2)(%eax)
- mov %edx,(-1-2-4)(%eax,%ecx)
- cmp $14,%ecx
- jbe 1f
-
- mov %edx,(1+2+4)(%eax)
- mov %edx,(1+2+4+4)(%eax)
- mov %edx,(-1-2-4-8)(%eax,%ecx)
- mov %edx,(-1-2-4-4)(%eax,%ecx)
- cmp $30,%ecx
- jbe 1f
-
- mov %edx,(1+2+4+8)(%eax)
- mov %edx,(1+2+4+8+4)(%eax)
- mov %edx,(1+2+4+8+8)(%eax)
- mov %edx,(1+2+4+8+12)(%eax)
- mov %edx,(-1-2-4-8-16)(%eax,%ecx)
- mov %edx,(-1-2-4-8-12)(%eax,%ecx)
- mov %edx,(-1-2-4-8-8)(%eax,%ecx)
- mov %edx,(-1-2-4-8-4)(%eax,%ecx)
-
-1: ret
-
-2: movzbl 8(%esp),%eax
- mov %edi,12(%esp)
- imul $0x1010101,%eax
- mov 4(%esp),%edi
- test $15,%edi
- mov %eax,-4(%edi,%ecx)
- jnz 2f
-
-1: shr $2, %ecx
- rep
- stosl
- mov 4(%esp),%eax
- mov 12(%esp),%edi
- ret
-
-2: xor %edx,%edx
- sub %edi,%edx
- and $15,%edx
- mov %eax,(%edi)
- mov %eax,4(%edi)
- mov %eax,8(%edi)
- mov %eax,12(%edi)
- sub %edx,%ecx
- add %edx,%edi
- jmp 1b
diff --git a/na/lib/string/index.c b/na/lib/string/index.c
deleted file mode 100644
index 8f6894c..0000000
--- a/na/lib/string/index.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _BSD_SOURCE
-#include <string.h>
-#include <strings.h>
-
-char *index(const char *s, int c)
-{
- return strchr(s, c);
-}
diff --git a/na/lib/string/memccpy.c b/na/lib/string/memccpy.c
deleted file mode 100644
index 99f146f..0000000
--- a/na/lib/string/memccpy.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-void *memccpy(void *restrict dest, const void *restrict src, int c, size_t n)
-{
- unsigned char *d = dest;
- const unsigned char *s = src;
-
- c = (unsigned char)c;
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- word *wd;
- const word *ws;
- if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
- for (; ((uintptr_t)s & ALIGN) && n && (*d=*s)!=c; n--, s++, d++);
- if ((uintptr_t)s & ALIGN) goto tail;
- size_t k = ONES * c;
- wd=(void *)d; ws=(const void *)s;
- for (; n>=sizeof(size_t) && !HASZERO(*ws^k);
- n-=sizeof(size_t), ws++, wd++) *wd = *ws;
- d=(void *)wd; s=(const void *)ws;
- }
-#endif
- for (; n && (*d=*s)!=c; n--, s++, d++);
-tail:
- if (n) return d+1;
- return 0;
-}
diff --git a/na/lib/string/memchr.c b/na/lib/string/memchr.c
deleted file mode 100644
index d0f670f..0000000
--- a/na/lib/string/memchr.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define SS (sizeof(size_t))
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-void *memchr(const void *src, int c, size_t n)
-{
- const unsigned char *s = src;
- c = (unsigned char)c;
-#ifdef __GNUC__
- for (; ((uintptr_t)s & ALIGN) && n && *s != c; s++, n--);
- if (n && *s != c) {
- typedef size_t __attribute__((__may_alias__)) word;
- const word *w;
- size_t k = ONES * c;
- for (w = (const void *)s; n>=SS && !HASZERO(*w^k); w++, n-=SS);
- s = (const void *)w;
- }
-#endif
- for (; n && *s != c; s++, n--);
- return n ? (void *)s : 0;
-}
diff --git a/na/lib/string/memcmp.c b/na/lib/string/memcmp.c
deleted file mode 100644
index 15606f4..0000000
--- a/na/lib/string/memcmp.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-int memcmp(const void *vl, const void *vr, size_t n)
-{
- const unsigned char *l=vl, *r=vr;
- for (; n && *l == *r; n--, l++, r++);
- return n ? *l-*r : 0;
-}
diff --git a/na/lib/string/memcpy.c b/na/lib/string/memcpy.c
deleted file mode 100644
index de26a00..0000000
--- a/na/lib/string/memcpy.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-#include <endian.h>
-
-void *memcpy(void *restrict dest, const void *restrict src, size_t n)
-{
- unsigned char *d = dest;
- const unsigned char *s = src;
-
-#ifdef __GNUC__
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define LS >>
-#define RS <<
-#else
-#define LS <<
-#define RS >>
-#endif
-
- typedef uint32_t __attribute__((__may_alias__)) u32;
- uint32_t w, x;
-
- for (; (uintptr_t)s % 4 && n; n--) *d++ = *s++;
-
- if ((uintptr_t)d % 4 == 0) {
- for (; n>=16; s+=16, d+=16, n-=16) {
- *(u32 *)(d+0) = *(u32 *)(s+0);
- *(u32 *)(d+4) = *(u32 *)(s+4);
- *(u32 *)(d+8) = *(u32 *)(s+8);
- *(u32 *)(d+12) = *(u32 *)(s+12);
- }
- if (n&8) {
- *(u32 *)(d+0) = *(u32 *)(s+0);
- *(u32 *)(d+4) = *(u32 *)(s+4);
- d += 8; s += 8;
- }
- if (n&4) {
- *(u32 *)(d+0) = *(u32 *)(s+0);
- d += 4; s += 4;
- }
- if (n&2) {
- *d++ = *s++; *d++ = *s++;
- }
- if (n&1) {
- *d = *s;
- }
- return dest;
- }
-
- if (n >= 32) switch ((uintptr_t)d % 4) {
- case 1:
- w = *(u32 *)s;
- *d++ = *s++;
- *d++ = *s++;
- *d++ = *s++;
- n -= 3;
- for (; n>=17; s+=16, d+=16, n-=16) {
- x = *(u32 *)(s+1);
- *(u32 *)(d+0) = (w LS 24) | (x RS 8);
- w = *(u32 *)(s+5);
- *(u32 *)(d+4) = (x LS 24) | (w RS 8);
- x = *(u32 *)(s+9);
- *(u32 *)(d+8) = (w LS 24) | (x RS 8);
- w = *(u32 *)(s+13);
- *(u32 *)(d+12) = (x LS 24) | (w RS 8);
- }
- break;
- case 2:
- w = *(u32 *)s;
- *d++ = *s++;
- *d++ = *s++;
- n -= 2;
- for (; n>=18; s+=16, d+=16, n-=16) {
- x = *(u32 *)(s+2);
- *(u32 *)(d+0) = (w LS 16) | (x RS 16);
- w = *(u32 *)(s+6);
- *(u32 *)(d+4) = (x LS 16) | (w RS 16);
- x = *(u32 *)(s+10);
- *(u32 *)(d+8) = (w LS 16) | (x RS 16);
- w = *(u32 *)(s+14);
- *(u32 *)(d+12) = (x LS 16) | (w RS 16);
- }
- break;
- case 3:
- w = *(u32 *)s;
- *d++ = *s++;
- n -= 1;
- for (; n>=19; s+=16, d+=16, n-=16) {
- x = *(u32 *)(s+3);
- *(u32 *)(d+0) = (w LS 8) | (x RS 24);
- w = *(u32 *)(s+7);
- *(u32 *)(d+4) = (x LS 8) | (w RS 24);
- x = *(u32 *)(s+11);
- *(u32 *)(d+8) = (w LS 8) | (x RS 24);
- w = *(u32 *)(s+15);
- *(u32 *)(d+12) = (x LS 8) | (w RS 24);
- }
- break;
- }
- if (n&16) {
- *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
- *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
- *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
- *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
- }
- if (n&8) {
- *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
- *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
- }
- if (n&4) {
- *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
- }
- if (n&2) {
- *d++ = *s++; *d++ = *s++;
- }
- if (n&1) {
- *d = *s;
- }
- return dest;
-#endif
-
- for (; n; n--) *d++ = *s++;
- return dest;
-}
diff --git a/na/lib/string/memmem.c b/na/lib/string/memmem.c
deleted file mode 100644
index cfd23bb..0000000
--- a/na/lib/string/memmem.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <stdint.h>
-
-static char *twobyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
- for (h+=2, k-=2; k; k--, hw = hw<<8 | *h++)
- if (hw == nw) return (char *)h-2;
- return hw == nw ? (char *)h-2 : 0;
-}
-
-static char *threebyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8;
- uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8;
- for (h+=3, k-=3; k; k--, hw = (hw|*h++)<<8)
- if (hw == nw) return (char *)h-3;
- return hw == nw ? (char *)h-3 : 0;
-}
-
-static char *fourbyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
- uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
- for (h+=4, k-=4; k; k--, hw = hw<<8 | *h++)
- if (hw == nw) return (char *)h-4;
- return hw == nw ? (char *)h-4 : 0;
-}
-
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#define MIN(a,b) ((a)<(b)?(a):(b))
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-static char *twoway_memmem(const unsigned char *h, const unsigned char *z, const unsigned char *n, size_t l)
-{
- size_t i, ip, jp, k, p, ms, p0, mem, mem0;
- size_t byteset[32 / sizeof(size_t)] = { 0 };
- size_t shift[256];
-
- /* Computing length of needle and fill shift table */
- for (i=0; i<l; i++)
- BITOP(byteset, n[i], |=), shift[n[i]] = i+1;
-
- /* Compute maximal suffix */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] > n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- ms = ip;
- p0 = p;
-
- /* And with the opposite comparison */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] < n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- if (ip+1 > ms+1) ms = ip;
- else p = p0;
-
- /* Periodic needle? */
- if (memcmp(n, n+p, ms+1)) {
- mem0 = 0;
- p = MAX(ms, l-ms-1) + 1;
- } else mem0 = l-p;
- mem = 0;
-
- /* Search loop */
- for (;;) {
- /* If remainder of haystack is shorter than needle, done */
- if (z-h < l) return 0;
-
- /* Check last byte first; advance by shift on mismatch */
- if (BITOP(byteset, h[l-1], &)) {
- k = l-shift[h[l-1]];
- if (k) {
- if (k < mem) k = mem;
- h += k;
- mem = 0;
- continue;
- }
- } else {
- h += l;
- mem = 0;
- continue;
- }
-
- /* Compare right half */
- for (k=MAX(ms+1,mem); k<l && n[k] == h[k]; k++);
- if (k < l) {
- h += k-ms;
- mem = 0;
- continue;
- }
- /* Compare left half */
- for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
- if (k <= mem) return (char *)h;
- h += p;
- mem = mem0;
- }
-}
-
-void *memmem(const void *h0, size_t k, const void *n0, size_t l)
-{
- const unsigned char *h = h0, *n = n0;
-
- /* Return immediately on empty needle */
- if (!l) return (void *)h;
-
- /* Return immediately when needle is longer than haystack */
- if (k<l) return 0;
-
- /* Use faster algorithms for short needles */
- h = memchr(h0, *n, k);
- if (!h || l==1) return (void *)h;
- k -= h - (const unsigned char *)h0;
- if (k<l) return 0;
- if (l==2) return twobyte_memmem(h, k, n);
- if (l==3) return threebyte_memmem(h, k, n);
- if (l==4) return fourbyte_memmem(h, k, n);
-
- return twoway_memmem(h, h+k, n, l);
-}
diff --git a/na/lib/string/memmove.c b/na/lib/string/memmove.c
deleted file mode 100644
index 56e1a81..0000000
--- a/na/lib/string/memmove.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-
-#ifdef __GNUC__
-typedef __attribute__((__may_alias__)) size_t WT;
-#define WS (sizeof(WT))
-#endif
-
-void *memmove(void *dest, const void *src, size_t n)
-{
- char *d = dest;
- const char *s = src;
-
- if (d==s) return d;
- if ((uintptr_t)s-(uintptr_t)d-n <= -2*n) return memcpy(d, s, n);
-
- if (d<s) {
-#ifdef __GNUC__
- if ((uintptr_t)s % WS == (uintptr_t)d % WS) {
- while ((uintptr_t)d % WS) {
- if (!n--) return dest;
- *d++ = *s++;
- }
- for (; n>=WS; n-=WS, d+=WS, s+=WS) *(WT *)d = *(WT *)s;
- }
-#endif
- for (; n; n--) *d++ = *s++;
- } else {
-#ifdef __GNUC__
- if ((uintptr_t)s % WS == (uintptr_t)d % WS) {
- while ((uintptr_t)(d+n) % WS) {
- if (!n--) return dest;
- d[n] = s[n];
- }
- while (n>=WS) n-=WS, *(WT *)(d+n) = *(WT *)(s+n);
- }
-#endif
- while (n) n--, d[n] = s[n];
- }
-
- return dest;
-}
diff --git a/na/lib/string/mempcpy.c b/na/lib/string/mempcpy.c
deleted file mode 100644
index 1f33e45..0000000
--- a/na/lib/string/mempcpy.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _GNU_SOURCE
-#include <string.h>
-
-void *mempcpy(void *dest, const void *src, size_t n)
-{
- return (char *)memcpy(dest, src, n) + n;
-}
diff --git a/na/lib/string/memrchr.c b/na/lib/string/memrchr.c
deleted file mode 100644
index 8d5c742..0000000
--- a/na/lib/string/memrchr.c
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-void *__memrchr(const void *m, int c, size_t n)
-{
- const unsigned char *s = m;
- c = (unsigned char)c;
- while (n--) if (s[n]==c) return (void *)(s+n);
- return 0;
-}
-
-weak_alias(__memrchr, memrchr);
diff --git a/na/lib/string/memset.c b/na/lib/string/memset.c
deleted file mode 100644
index e780c71..0000000
--- a/na/lib/string/memset.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-
-void *memset(void *dest, int c, size_t n)
-{
- unsigned char *s = dest;
- size_t k;
-
- /* Fill head and tail with minimal branching. Each
- * conditional ensures that all the subsequently used
- * offsets are well-defined and in the dest region. */
-
- if (!n) return dest;
- s[0] = c;
- s[n-1] = c;
- if (n <= 2) return dest;
- s[1] = c;
- s[2] = c;
- s[n-2] = c;
- s[n-3] = c;
- if (n <= 6) return dest;
- s[3] = c;
- s[n-4] = c;
- if (n <= 8) return dest;
-
- /* Advance pointer to align it at a 4-byte boundary,
- * and truncate n to a multiple of 4. The previous code
- * already took care of any head/tail that get cut off
- * by the alignment. */
-
- k = -(uintptr_t)s & 3;
- s += k;
- n -= k;
- n &= -4;
-
-#ifdef __GNUC__
- typedef uint32_t __attribute__((__may_alias__)) u32;
- typedef uint64_t __attribute__((__may_alias__)) u64;
-
- u32 c32 = ((u32)-1)/255 * (unsigned char)c;
-
- /* In preparation to copy 32 bytes at a time, aligned on
- * an 8-byte bounary, fill head/tail up to 28 bytes each.
- * As in the initial byte-based head/tail fill, each
- * conditional below ensures that the subsequent offsets
- * are valid (e.g. !(n<=24) implies n>=28). */
-
- *(u32 *)(s+0) = c32;
- *(u32 *)(s+n-4) = c32;
- if (n <= 8) return dest;
- *(u32 *)(s+4) = c32;
- *(u32 *)(s+8) = c32;
- *(u32 *)(s+n-12) = c32;
- *(u32 *)(s+n-8) = c32;
- if (n <= 24) return dest;
- *(u32 *)(s+12) = c32;
- *(u32 *)(s+16) = c32;
- *(u32 *)(s+20) = c32;
- *(u32 *)(s+24) = c32;
- *(u32 *)(s+n-28) = c32;
- *(u32 *)(s+n-24) = c32;
- *(u32 *)(s+n-20) = c32;
- *(u32 *)(s+n-16) = c32;
-
- /* Align to a multiple of 8 so we can fill 64 bits at a time,
- * and avoid writing the same bytes twice as much as is
- * practical without introducing additional branching. */
-
- k = 24 + ((uintptr_t)s & 4);
- s += k;
- n -= k;
-
- /* If this loop is reached, 28 tail bytes have already been
- * filled, so any remainder when n drops below 32 can be
- * safely ignored. */
-
- u64 c64 = c32 | ((u64)c32 << 32);
- for (; n >= 32; n-=32, s+=32) {
- *(u64 *)(s+0) = c64;
- *(u64 *)(s+8) = c64;
- *(u64 *)(s+16) = c64;
- *(u64 *)(s+24) = c64;
- }
-#else
- /* Pure C fallback with no aliasing violations. */
- for (; n; n--, s++) *s = c;
-#endif
-
- return dest;
-}
diff --git a/na/lib/string/rindex.c b/na/lib/string/rindex.c
deleted file mode 100644
index 897e19e..0000000
--- a/na/lib/string/rindex.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _BSD_SOURCE
-#include <string.h>
-#include <strings.h>
-
-char *rindex(const char *s, int c)
-{
- return strrchr(s, c);
-}
diff --git a/na/lib/string/stpcpy.c b/na/lib/string/stpcpy.c
deleted file mode 100644
index 0c1f1b4..0000000
--- a/na/lib/string/stpcpy.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t))
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-char *__stpcpy(char *restrict d, const char *restrict s)
-{
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- word *wd;
- const word *ws;
- if ((uintptr_t)s % ALIGN == (uintptr_t)d % ALIGN) {
- for (; (uintptr_t)s % ALIGN; s++, d++)
- if (!(*d=*s)) return d;
- wd=(void *)d; ws=(const void *)s;
- for (; !HASZERO(*ws); *wd++ = *ws++);
- d=(void *)wd; s=(const void *)ws;
- }
-#endif
- for (; (*d=*s); s++, d++);
-
- return d;
-}
-
-weak_alias(__stpcpy, stpcpy);
diff --git a/na/lib/string/stpncpy.c b/na/lib/string/stpncpy.c
deleted file mode 100644
index ca2ef78..0000000
--- a/na/lib/string/stpncpy.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-char *__stpncpy(char *restrict d, const char *restrict s, size_t n)
-{
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- word *wd;
- const word *ws;
- if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
- for (; ((uintptr_t)s & ALIGN) && n && (*d=*s); n--, s++, d++);
- if (!n || !*s) goto tail;
- wd=(void *)d; ws=(const void *)s;
- for (; n>=sizeof(size_t) && !HASZERO(*ws);
- n-=sizeof(size_t), ws++, wd++) *wd = *ws;
- d=(void *)wd; s=(const void *)ws;
- }
-#endif
- for (; n && (*d=*s); n--, s++, d++);
-tail:
- memset(d, 0, n);
- return d;
-}
-
-weak_alias(__stpncpy, stpncpy);
-
diff --git a/na/lib/string/strcat.c b/na/lib/string/strcat.c
deleted file mode 100644
index 723c2ff..0000000
--- a/na/lib/string/strcat.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strcat(char *restrict dest, const char *restrict src)
-{
- strcpy(dest + strlen(dest), src);
- return dest;
-}
diff --git a/na/lib/string/strchr.c b/na/lib/string/strchr.c
deleted file mode 100644
index ddeca01..0000000
--- a/na/lib/string/strchr.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strchr(const char *s, int c)
-{
- char *r = __strchrnul(s, c);
- return *(unsigned char *)r == (unsigned char)c ? r : 0;
-}
diff --git a/na/lib/string/strchrnul.c b/na/lib/string/strchrnul.c
deleted file mode 100644
index 6fc86d1..0000000
--- a/na/lib/string/strchrnul.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t))
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-char *__strchrnul(const char *s, int c)
-{
- c = (unsigned char)c;
- if (!c) return (char *)s + strlen(s);
-
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- const word *w;
- for (; (uintptr_t)s % ALIGN; s++)
- if (!*s || *(unsigned char *)s == c) return (char *)s;
- size_t k = ONES * c;
- for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++);
- s = (void *)w;
-#endif
- for (; *s && *(unsigned char *)s != c; s++);
- return (char *)s;
-}
-
-weak_alias(__strchrnul, strchrnul);
diff --git a/na/lib/string/strcmp.c b/na/lib/string/strcmp.c
deleted file mode 100644
index afcb1b7..0000000
--- a/na/lib/string/strcmp.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-int strcmp(const char *l, const char *r)
-{
- for (; *l==*r && *l; l++, r++);
- return *(unsigned char *)l - *(unsigned char *)r;
-}
diff --git a/na/lib/string/strcpy.c b/na/lib/string/strcpy.c
deleted file mode 100644
index a761577..0000000
--- a/na/lib/string/strcpy.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strcpy(char *restrict dest, const char *restrict src)
-{
- __stpcpy(dest, src);
- return dest;
-}
diff --git a/na/lib/string/strcspn.c b/na/lib/string/strcspn.c
deleted file mode 100644
index ba55fe6..0000000
--- a/na/lib/string/strcspn.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-size_t strcspn(const char *s, const char *c)
-{
- const char *a = s;
- size_t byteset[32/sizeof(size_t)];
-
- if (!c[0] || !c[1]) return __strchrnul(s, *c)-a;
-
- memset(byteset, 0, sizeof byteset);
- for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++);
- for (; *s && !BITOP(byteset, *(unsigned char *)s, &); s++);
- return s-a;
-}
diff --git a/na/lib/string/strlcat.c b/na/lib/string/strlcat.c
deleted file mode 100644
index a61821d..0000000
--- a/na/lib/string/strlcat.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _BSD_SOURCE
-#include <string.h>
-
-size_t strlcat(char *d, const char *s, size_t n)
-{
- size_t l = strnlen(d, n);
- if (l == n) return l + strlen(s);
- return l + strlcpy(d+l, s, n-l);
-}
diff --git a/na/lib/string/strlcpy.c b/na/lib/string/strlcpy.c
deleted file mode 100644
index c2a5142..0000000
--- a/na/lib/string/strlcpy.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _BSD_SOURCE
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-size_t strlcpy(char *d, const char *s, size_t n)
-{
- char *d0 = d;
- size_t *wd;
-
- if (!n--) goto finish;
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- const word *ws;
- if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
- for (; ((uintptr_t)s & ALIGN) && n && (*d=*s); n--, s++, d++);
- if (n && *s) {
- wd=(void *)d; ws=(const void *)s;
- for (; n>=sizeof(size_t) && !HASZERO(*ws);
- n-=sizeof(size_t), ws++, wd++) *wd = *ws;
- d=(void *)wd; s=(const void *)ws;
- }
- }
-#endif
- for (; n && (*d=*s); n--, s++, d++);
- *d = 0;
-finish:
- return d-d0 + strlen(s);
-}
diff --git a/na/lib/string/strlen.c b/na/lib/string/strlen.c
deleted file mode 100644
index 76e14f2..0000000
--- a/na/lib/string/strlen.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t))
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-size_t strlen(const char *s)
-{
- const char *a = s;
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- const word *w;
- for (; (uintptr_t)s % ALIGN; s++) if (!*s) return s-a;
- for (w = (const void *)s; !HASZERO(*w); w++);
- s = (const void *)w;
-#endif
- for (; *s; s++);
- return s-a;
-}
diff --git a/na/lib/string/strncat.c b/na/lib/string/strncat.c
deleted file mode 100644
index a144e37..0000000
--- a/na/lib/string/strncat.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strncat(char *restrict d, const char *restrict s, size_t n)
-{
- char *a = d;
- d += strlen(d);
- while (n && *s) n--, *d++ = *s++;
- *d++ = 0;
- return a;
-}
diff --git a/na/lib/string/strncmp.c b/na/lib/string/strncmp.c
deleted file mode 100644
index b98473b..0000000
--- a/na/lib/string/strncmp.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-int strncmp(const char *_l, const char *_r, size_t n)
-{
- const unsigned char *l=(void *)_l, *r=(void *)_r;
- if (!n--) return 0;
- for (; *l && *r && n && *l == *r ; l++, r++, n--);
- return *l - *r;
-}
diff --git a/na/lib/string/strncpy.c b/na/lib/string/strncpy.c
deleted file mode 100644
index a828465..0000000
--- a/na/lib/string/strncpy.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strncpy(char *restrict d, const char *restrict s, size_t n)
-{
- __stpncpy(d, s, n);
- return d;
-}
diff --git a/na/lib/string/strnlen.c b/na/lib/string/strnlen.c
deleted file mode 100644
index 8931c52..0000000
--- a/na/lib/string/strnlen.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-size_t strnlen(const char *s, size_t n)
-{
- const char *p = memchr(s, 0, n);
- return p ? p-s : n;
-}
diff --git a/na/lib/string/strpbrk.c b/na/lib/string/strpbrk.c
deleted file mode 100644
index 82fbc92..0000000
--- a/na/lib/string/strpbrk.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strpbrk(const char *s, const char *b)
-{
- s += strcspn(s, b);
- return *s ? (char *)s : 0;
-}
diff --git a/na/lib/string/strrchr.c b/na/lib/string/strrchr.c
deleted file mode 100644
index a45b459..0000000
--- a/na/lib/string/strrchr.c
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strrchr(const char *s, int c)
-{
- return __memrchr(s, c, strlen(s) + 1);
-}
diff --git a/na/lib/string/strsep.c b/na/lib/string/strsep.c
deleted file mode 100644
index 2d513f4..0000000
--- a/na/lib/string/strsep.c
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#define _GNU_SOURCE
-#include <string.h>
-
-char *strsep(char **str, const char *sep)
-{
- char *s = *str, *end;
- if (!s) return NULL;
- end = s + strcspn(s, sep);
- if (*end) *end++ = 0;
- else end = 0;
- *str = end;
- return s;
-}
diff --git a/na/lib/string/strspn.c b/na/lib/string/strspn.c
deleted file mode 100644
index f27710d..0000000
--- a/na/lib/string/strspn.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-size_t strspn(const char *s, const char *c)
-{
- const char *a = s;
- size_t byteset[32/sizeof(size_t)] = { 0 };
-
- if (!c[0]) return 0;
- if (!c[1]) {
- for (; *s == *c; s++);
- return s-a;
- }
-
- for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++);
- for (; *s && BITOP(byteset, *(unsigned char *)s, &); s++);
- return s-a;
-}
diff --git a/na/lib/string/strstr.c b/na/lib/string/strstr.c
deleted file mode 100644
index 07e2982..0000000
--- a/na/lib/string/strstr.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-#include <stdint.h>
-
-static char *twobyte_strstr(const unsigned char *h, const unsigned char *n)
-{
- uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
- for (h++; *h && hw != nw; hw = hw<<8 | *++h);
- return *h ? (char *)h-1 : 0;
-}
-
-static char *threebyte_strstr(const unsigned char *h, const unsigned char *n)
-{
- uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8;
- uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8;
- for (h+=2; *h && hw != nw; hw = (hw|*++h)<<8);
- return *h ? (char *)h-2 : 0;
-}
-
-static char *fourbyte_strstr(const unsigned char *h, const unsigned char *n)
-{
- uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
- uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
- for (h+=3; *h && hw != nw; hw = hw<<8 | *++h);
- return *h ? (char *)h-3 : 0;
-}
-
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#define MIN(a,b) ((a)<(b)?(a):(b))
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-static char *twoway_strstr(const unsigned char *h, const unsigned char *n)
-{
- const unsigned char *z;
- size_t l, ip, jp, k, p, ms, p0, mem, mem0;
- size_t byteset[32 / sizeof(size_t)] = { 0 };
- size_t shift[256];
-
- /* Computing length of needle and fill shift table */
- for (l=0; n[l] && h[l]; l++)
- BITOP(byteset, n[l], |=), shift[n[l]] = l+1;
- if (n[l]) return 0; /* hit the end of h */
-
- /* Compute maximal suffix */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] > n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- ms = ip;
- p0 = p;
-
- /* And with the opposite comparison */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] < n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- if (ip+1 > ms+1) ms = ip;
- else p = p0;
-
- /* Periodic needle? */
- if (memcmp(n, n+p, ms+1)) {
- mem0 = 0;
- p = MAX(ms, l-ms-1) + 1;
- } else mem0 = l-p;
- mem = 0;
-
- /* Initialize incremental end-of-haystack pointer */
- z = h;
-
- /* Search loop */
- for (;;) {
- /* Update incremental end-of-haystack pointer */
- if (z-h < l) {
- /* Fast estimate for MAX(l,63) */
- size_t grow = l | 63;
- const unsigned char *z2 = memchr(z, 0, grow);
- if (z2) {
- z = z2;
- if (z-h < l) return 0;
- } else z += grow;
- }
-
- /* Check last byte first; advance by shift on mismatch */
- if (BITOP(byteset, h[l-1], &)) {
- k = l-shift[h[l-1]];
- if (k) {
- if (k < mem) k = mem;
- h += k;
- mem = 0;
- continue;
- }
- } else {
- h += l;
- mem = 0;
- continue;
- }
-
- /* Compare right half */
- for (k=MAX(ms+1,mem); n[k] && n[k] == h[k]; k++);
- if (n[k]) {
- h += k-ms;
- mem = 0;
- continue;
- }
- /* Compare left half */
- for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
- if (k <= mem) return (char *)h;
- h += p;
- mem = mem0;
- }
-}
-
-char *strstr(const char *h, const char *n)
-{
- /* Return immediately on empty needle */
- if (!n[0]) return (char *)h;
-
- /* Use faster algorithms for short needles */
- h = strchr(h, *n);
- if (!h || !n[1]) return (char *)h;
- if (!h[1]) return 0;
- if (!n[2]) return twobyte_strstr((void *)h, (void *)n);
- if (!h[2]) return 0;
- if (!n[3]) return threebyte_strstr((void *)h, (void *)n);
- if (!h[3]) return 0;
- if (!n[4]) return fourbyte_strstr((void *)h, (void *)n);
-
- return twoway_strstr((void *)h, (void *)n);
-}
diff --git a/na/lib/string/strtok.c b/na/lib/string/strtok.c
deleted file mode 100644
index 86d9c97..0000000
--- a/na/lib/string/strtok.c
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strtok(char *restrict s, const char *restrict sep)
-{
- static char *p;
- if (!s && !(s = p)) return NULL;
- s += strspn(s, sep);
- if (!*s) return p = 0;
- p = s + strcspn(s, sep);
- if (*p) *p++ = 0;
- else p = 0;
- return s;
-}
diff --git a/na/lib/string/strtok_r.c b/na/lib/string/strtok_r.c
deleted file mode 100644
index a702703..0000000
--- a/na/lib/string/strtok_r.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Derived from musl libc (Copyright © 2005-2020 Rich Felker, et al.) */
-
-#include <string.h>
-
-char *strtok_r(char *restrict s, const char *restrict sep, char **restrict p)
-{
- if (!s && !(s = *p)) return NULL;
- s += strspn(s, sep);
- if (!*s) return *p = 0;
- *p = s + strcspn(s, sep);
- if (**p) *(*p)++ = 0;
- else *p = 0;
- return s;
-}
diff --git a/na/lib/string/x86_64/memcpy.s b/na/lib/string/x86_64/memcpy.s
deleted file mode 100644
index 3d960ef..0000000
--- a/na/lib/string/x86_64/memcpy.s
+++ /dev/null
@@ -1,25 +0,0 @@
-.global memcpy
-.global __memcpy_fwd
-.hidden __memcpy_fwd
-.type memcpy,@function
-memcpy:
-__memcpy_fwd:
- mov %rdi,%rax
- cmp $8,%rdx
- jc 1f
- test $7,%edi
- jz 1f
-2: movsb
- dec %rdx
- test $7,%edi
- jnz 2b
-1: mov %rdx,%rcx
- shr $3,%rcx
- rep
- movsq
- and $7,%edx
- jz 1f
-2: movsb
- dec %edx
- jnz 2b
-1: ret
diff --git a/na/lib/string/x86_64/memmove.s b/na/lib/string/x86_64/memmove.s
deleted file mode 100644
index 172c025..0000000
--- a/na/lib/string/x86_64/memmove.s
+++ /dev/null
@@ -1,16 +0,0 @@
-.global memmove
-.type memmove,@function
-memmove:
- mov %rdi,%rax
- sub %rsi,%rax
- cmp %rdx,%rax
-.hidden __memcpy_fwd
- jae __memcpy_fwd
- mov %rdx,%rcx
- lea -1(%rdi,%rdx),%rdi
- lea -1(%rsi,%rdx),%rsi
- std
- rep movsb
- cld
- lea 1(%rdi),%rax
- ret
diff --git a/na/lib/string/x86_64/memset.s b/na/lib/string/x86_64/memset.s
deleted file mode 100644
index 2d3f5e5..0000000
--- a/na/lib/string/x86_64/memset.s
+++ /dev/null
@@ -1,72 +0,0 @@
-.global memset
-.type memset,@function
-memset:
- movzbq %sil,%rax
- mov $0x101010101010101,%r8
- imul %r8,%rax
-
- cmp $126,%rdx
- ja 2f
-
- test %edx,%edx
- jz 1f
-
- mov %sil,(%rdi)
- mov %sil,-1(%rdi,%rdx)
- cmp $2,%edx
- jbe 1f
-
- mov %ax,1(%rdi)
- mov %ax,(-1-2)(%rdi,%rdx)
- cmp $6,%edx
- jbe 1f
-
- mov %eax,(1+2)(%rdi)
- mov %eax,(-1-2-4)(%rdi,%rdx)
- cmp $14,%edx
- jbe 1f
-
- mov %rax,(1+2+4)(%rdi)
- mov %rax,(-1-2-4-8)(%rdi,%rdx)
- cmp $30,%edx
- jbe 1f
-
- mov %rax,(1+2+4+8)(%rdi)
- mov %rax,(1+2+4+8+8)(%rdi)
- mov %rax,(-1-2-4-8-16)(%rdi,%rdx)
- mov %rax,(-1-2-4-8-8)(%rdi,%rdx)
- cmp $62,%edx
- jbe 1f
-
- mov %rax,(1+2+4+8+16)(%rdi)
- mov %rax,(1+2+4+8+16+8)(%rdi)
- mov %rax,(1+2+4+8+16+16)(%rdi)
- mov %rax,(1+2+4+8+16+24)(%rdi)
- mov %rax,(-1-2-4-8-16-32)(%rdi,%rdx)
- mov %rax,(-1-2-4-8-16-24)(%rdi,%rdx)
- mov %rax,(-1-2-4-8-16-16)(%rdi,%rdx)
- mov %rax,(-1-2-4-8-16-8)(%rdi,%rdx)
-
-1: mov %rdi,%rax
- ret
-
-2: test $15,%edi
- mov %rdi,%r8
- mov %rax,-8(%rdi,%rdx)
- mov %rdx,%rcx
- jnz 2f
-
-1: shr $3,%rcx
- rep
- stosq
- mov %r8,%rax
- ret
-
-2: xor %edx,%edx
- sub %edi,%edx
- and $15,%edx
- mov %rax,(%rdi)
- mov %rax,8(%rdi)
- sub %rdx,%rcx
- add %rdx,%rdi
- jmp 1b
diff --git a/na/mm/memblock.cc b/na/mm/memblock.cc
deleted file mode 100644
index aee0245..0000000
--- a/na/mm/memblock.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * memblock.cc — Boot-time physical memory allocator
- *
- * Simple region tracker: one memory region (from FDT) plus an array
- * of reserved regions. Allocation searches for gaps between reserved
- * regions. All operations are O(n_reserved), which is fine for the
- * ~20 allocations that happen during early boot.
- *
- * Reference: inspired by Linux kernel's mm/memblock.c
- */
-
-#include <lume/memblock.h>
-#include <lume/fdt.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/config.h>
-#include <lume/addr.h>
-
-extern "C" char _stext[], _kernel_end[];
-
-/* ------------------------------------------------------------------ */
-/* Module state */
-/* ------------------------------------------------------------------ */
-
-namespace {
-
-constexpr uint64 kMB = 1024ULL * 1024ULL;
-
-struct {
- uint64 mem_base;
- uint64 mem_size;
-
- struct {
- uint64 base;
- uint64 size;
- } reserved[kMemblockMaxRegions];
- int reserved_count;
- bool active;
-} g_mb;
-
-/* ------------------------------------------------------------------ */
-/* Internal helpers */
-/* ------------------------------------------------------------------ */
-
-/* Insert a reserved region, keeping the array sorted by base. */
-void insert_reserved(uint64 base, uint64 size)
-{
- if (g_mb.reserved_count >= kMemblockMaxRegions) {
- kernel_panic("memblock: too many reserved regions");
- }
-
- int i = g_mb.reserved_count;
- while (i > 0 && g_mb.reserved[i - 1].base > base) {
- g_mb.reserved[i] = g_mb.reserved[i - 1];
- i--;
- }
- g_mb.reserved[i] = {base, size};
- g_mb.reserved_count++;
-}
-
-/* Check if [base, base+size) overlaps any reserved region. */
-bool overlaps_reserved(uint64 base, uint64 size)
-{
- uint64 end = base + size;
- for (int i = 0; i < g_mb.reserved_count; i++) {
- uint64 rb = g_mb.reserved[i].base;
- uint64 re = rb + g_mb.reserved[i].size;
- if (base < re && end > rb) {
- return true;
- }
- }
- return false;
-}
-
-/* Find the end of the first reserved region that covers `addr`. */
-uint64 reserved_end_covering(uint64 addr)
-{
- for (int i = 0; i < g_mb.reserved_count; i++) {
- uint64 rb = g_mb.reserved[i].base;
- uint64 re = rb + g_mb.reserved[i].size;
- if (addr >= rb && addr < re) {
- return re;
- }
- }
- return addr;
-}
-
-} // namespace
-
-/* ------------------------------------------------------------------ */
-/* Public API */
-/* ------------------------------------------------------------------ */
-
-void memblock_init(uint64 fdt_paddr)
-{
-
- /* 1. Get memory layout from FDT */
- uint64 mem_base = 0;
- uint64 mem_size = 0;
- fdt_early_get_mem_info(&mem_base, &mem_size);
- if (mem_size == 0) {
- kernel_panic("memblock_init: FDT reports zero memory");
- }
-
- g_mb.mem_base = mem_base;
- g_mb.mem_size = mem_size;
- g_mb.reserved_count = 0;
- g_mb.active = true;
-
- /* 2. Reserve firmware region [mem_base, kPhysStart).
- *
- * Everything below the kernel's linked load address is firmware
- * territory (OpenSBI), which is PMP-protected. */
- {
- uint64 fw_end = kPhysStart;
- if (fw_end > mem_base) {
- memblock_reserve(mem_base, fw_end - mem_base);
- }
- }
-
- /* 2b. Reserve stale pre-relocation kernel image.
- *
- * After physical KASLR, the kernel is relocated from its original
- * load address (kPhysStart) to a new PA. The old copy at
- * [kPhysStart, kPhysStart + kernel_size) must not be allocated —
- * it contains stale code/data that would corrupt page tables or
- * other structures if reused. */
- {
- uint64 ks_pa = boot_va_to_pa(virt_addr(
- reinterpret_cast<uint64>(_stext))).raw;
- if (ks_pa != kPhysStart) {
- /* Kernel was relocated — reserve the original location */
- uint64 kernel_size = boot_va_to_pa(virt_addr(
- reinterpret_cast<uint64>(_kernel_end))).raw - ks_pa;
- memblock_reserve(kPhysStart, kernel_size);
- }
- }
-
- /* 3. Reserve kernel image [_stext, _kernel_end) */
- uint64 ks_pa = boot_va_to_pa(virt_addr(
- reinterpret_cast<uint64>(_stext))).raw;
- uint64 ke_pa = boot_va_to_pa(virt_addr(
- reinterpret_cast<uint64>(_kernel_end))).raw;
- memblock_reserve(ks_pa, ke_pa - ks_pa);
-
- /* 4. Reserve FDT blob — it lives in RAM and must not be overwritten
- * by vmemmap/page_map or buddy before fdt_unflatten() copies it. */
- {
- struct fdt_hdr { uint32 magic; uint32 totalsize; };
- const auto* fh = reinterpret_cast<const fdt_hdr*>(fdt_paddr);
- uint32 fdt_size = ((fh->totalsize & 0xFF) << 24) |
- (((fh->totalsize >> 8) & 0xFF) << 16) |
- (((fh->totalsize >> 16) & 0xFF) << 8) |
- ((fh->totalsize >> 24) & 0xFF);
- memblock_reserve(fdt_paddr, fdt_size);
- kprintf("[memblock] FDT reserved [0x%llx, 0x%llx)\n",
- fdt_paddr, fdt_paddr + fdt_size);
- }
-
- kprintf("[memblock] RAM [0x%llx, 0x%llx) = %llu MB\n",
- mem_base, mem_base + mem_size, mem_size / kMB);
- kprintf("[memblock] kernel reserved [0x%llx, 0x%llx)\n", ks_pa, ke_pa);
-}
-
-void memblock_reserve(uint64 base, uint64 size)
-{
- uint64 aligned_base = page_align_down(base);
- uint64 aligned_end = page_align_up(base + size);
- insert_reserved(aligned_base, aligned_end - aligned_base);
-}
-
-uint64 memblock_alloc_top(uint64 size, uint64 align)
-{
- uint64 mem_end = g_mb.mem_base + g_mb.mem_size;
-
- /* Search from top of RAM downward */
- uint64 end = page_align_down(mem_end);
- uint64 start = (end - size) & ~(align - 1);
-
- while (start >= g_mb.mem_base && start + size <= mem_end) {
- if (!overlaps_reserved(start, size)) {
- memblock_reserve(start, size);
- return start;
- }
- if (start < align) {
- break;
- }
- start -= align;
- }
-
- kernel_panic("memblock_alloc_top: out of memory");
- return 0;
-}
-
-uint64 memblock_alloc(uint64 size, uint64 align)
-{
- uint64 mem_end = g_mb.mem_base + g_mb.mem_size;
- uint64 start = page_align_up(g_mb.mem_base);
-
- while (start + size <= mem_end) {
- start = (start + align - 1) & ~(align - 1);
- if (start + size > mem_end) {
- break;
- }
-
- if (!overlaps_reserved(start, size)) {
- memblock_reserve(start, size);
- return start;
- }
-
- /* Skip past the overlapping reserved region */
- uint64 skip = reserved_end_covering(start);
- if (skip <= start) {
- start += align;
- } else {
- start = skip;
- }
- }
-
- kernel_panic("memblock_alloc: out of memory");
- return 0;
-}
-
-uint64 memblock_get_mem_base() { return g_mb.mem_base; }
-uint64 memblock_get_mem_size() { return g_mb.mem_size; }
-
-bool memblock_is_active() { return g_mb.active; }
-void memblock_retire() { g_mb.active = false; }
-
-void memblock_for_each_free(void (*cb)(uint64 base, uint64 size, void* ctx),
- void* ctx)
-{
- uint64 pos = g_mb.mem_base;
- uint64 mem_end = g_mb.mem_base + g_mb.mem_size;
-
- for (int i = 0; i < g_mb.reserved_count && pos < mem_end; i++) {
- uint64 rb = g_mb.reserved[i].base;
- if (rb < pos) {
- uint64 re = g_mb.reserved[i].base + g_mb.reserved[i].size;
- if (re > pos) {
- pos = re;
- }
- continue;
- }
- if (rb > pos) {
- cb(pos, rb - pos, ctx);
- }
- pos = g_mb.reserved[i].base + g_mb.reserved[i].size;
- }
-
- if (pos < mem_end) {
- cb(pos, mem_end - pos, ctx);
- }
-}
diff --git a/na/mm/module.mk b/na/mm/module.mk
deleted file mode 100644
index 93cef80..0000000
--- a/na/mm/module.mk
+++ /dev/null
@@ -1,7 +0,0 @@
-SRCS_CC += mm/memblock.cc
-SRCS_CC += mm/pmm.cc
-SRCS_CC += mm/slab.cc
-SRCS_CC += mm/vmm.cc
-SRCS_CC += mm/vma.cc
-SRCS_CC += mm/page_cache.cc
-SRCS_CC += mm/tlb.cc
diff --git a/na/mm/page_cache.cc b/na/mm/page_cache.cc
deleted file mode 100644
index 25c6a60..0000000
--- a/na/mm/page_cache.cc
+++ /dev/null
@@ -1,252 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * page_cache.cc — Minimal page cache implementation
- *
- * Open-addressing hash table indexed by (mapping, index) pairs.
- * Fixed-size bucket array allocated at init time via slab.
- *
- * This is intentionally simple: no LRU, no writeback, no eviction.
- * The interface in page_cache.h is designed so that upgrading to a
- * radix tree + LRU scheme later only changes this file.
- *
- * Concurrency: protected by a single spinlock. Acceptable for
- * initial bring-up; will be replaced by per-bucket or RCU-based
- * locking when contention becomes measurable.
- */
-
-#include <lume/page_cache.h>
-#include <lume/page.h>
-#include <lume/pmm.h>
-#include <lume/errno.h>
-#include <lume/new.h>
-#include <kernel/sync/spinlock.h>
-
-/* ------------------------------------------------------------------ */
-/* Hash table internals */
-/* ------------------------------------------------------------------ */
-
-struct CacheEntry {
- void* mapping; // nullptr = empty slot
- uint64 index;
- Page* page;
-};
-
-namespace {
-
-constexpr uint64 kCacheBuckets = 1024;
-
-CacheEntry g_buckets[kCacheBuckets];
-Spinlock g_cache_lock;
-
-uint64 hash_key(void* mapping, uint64 index)
-{
- uint64 m = reinterpret_cast<uint64>(mapping);
- /* FNV-1a inspired mixing */
- uint64 h = m ^ (index * 0x517cc1b727220a95ULL);
- h ^= h >> 17;
- h *= 0xbf58476d1ce4e5b9ULL;
- h ^= h >> 31;
- return h % kCacheBuckets;
-}
-
-/* Linear probe to find an entry or an empty slot. */
-CacheEntry* find_slot(void* mapping, uint64 index)
-{
- uint64 start = hash_key(mapping, index);
- for (uint64 i = 0; i < kCacheBuckets; i++) {
- uint64 idx = (start + i) % kCacheBuckets;
- CacheEntry* e = &g_buckets[idx];
- if (e->mapping == nullptr) {
- return e; // Empty slot
- }
- if (e->mapping == mapping && e->index == index) {
- return e; // Found
- }
- }
- return nullptr; // Table full
-}
-
-/* Find an existing entry (not an empty slot). */
-CacheEntry* find_entry(void* mapping, uint64 index)
-{
- uint64 start = hash_key(mapping, index);
- for (uint64 i = 0; i < kCacheBuckets; i++) {
- uint64 idx = (start + i) % kCacheBuckets;
- CacheEntry* e = &g_buckets[idx];
- if (e->mapping == nullptr) {
- return nullptr; // Empty = not found
- }
- if (e->mapping == mapping && e->index == index) {
- return e;
- }
- }
- return nullptr;
-}
-
-/*
- * Remove an entry and fix up the linear-probe chain.
- * Standard open-addressing deletion with backward shift.
- */
-void remove_entry(CacheEntry* entry)
-{
- uint64 removed = static_cast<uint64>(entry - g_buckets);
- entry->mapping = nullptr;
- entry->page = nullptr;
-
- /* Backward-shift deletion to maintain probe chain integrity */
- uint64 cur = (removed + 1) % kCacheBuckets;
- while (g_buckets[cur].mapping != nullptr) {
- uint64 ideal = hash_key(g_buckets[cur].mapping, g_buckets[cur].index);
- /* Check if cur's ideal position is "before" removed in probe order */
- bool needs_shift = false;
- if (removed <= cur) {
- needs_shift = (ideal <= removed || ideal > cur);
- } else {
- needs_shift = (ideal <= removed && ideal > cur);
- }
-
- if (needs_shift) {
- g_buckets[removed] = g_buckets[cur];
- g_buckets[cur].mapping = nullptr;
- g_buckets[cur].page = nullptr;
- removed = cur;
- }
- cur = (cur + 1) % kCacheBuckets;
- }
-}
-
-} // namespace
-
-/* ------------------------------------------------------------------ */
-/* Public API */
-/* ------------------------------------------------------------------ */
-
-void page_cache_init()
-{
- g_cache_lock.init("page_cache");
- for (uint64 i = 0; i < kCacheBuckets; i++) {
- g_buckets[i].mapping = nullptr;
- g_buckets[i].index = 0;
- g_buckets[i].page = nullptr;
- }
-}
-
-Page* page_cache_get(void* mapping, uint64 index)
-{
- LockGuard guard(g_cache_lock);
- CacheEntry* e = find_entry(mapping, index);
- if (!e) {
- return nullptr;
- }
- page_incref(e->page);
- return e->page;
-}
-
-int page_cache_insert(void* mapping, uint64 index, Page* page)
-{
- if (!mapping || !page) {
- return -EINVAL;
- }
-
- LockGuard guard(g_cache_lock);
-
- /* Check for existing entry */
- CacheEntry* existing = find_entry(mapping, index);
- if (existing) {
- return -EEXIST;
- }
-
- /* Find an empty slot */
- CacheEntry* slot = find_slot(mapping, index);
- if (!slot || slot->mapping != nullptr) {
- return -ENOMEM; // Table full
- }
-
- slot->mapping = mapping;
- slot->index = index;
- slot->page = page;
- page_incref(page); // Cache holds a reference
-
- return 0;
-}
-
-void page_cache_put(Page* page)
-{
- if (!page) {
- return;
- }
- page_decref(page);
-}
-
-Page* page_cache_remove(void* mapping, uint64 index)
-{
- LockGuard guard(g_cache_lock);
- CacheEntry* e = find_entry(mapping, index);
- if (!e) {
- return nullptr;
- }
-
- Page* page = e->page;
- remove_entry(e);
- page_decref(page); // Release cache's reference
- return page;
-}
-
-void page_cache_invalidate(void* mapping)
-{
- if (!mapping) {
- return;
- }
-
- LockGuard guard(g_cache_lock);
-
- /* Scan all buckets — acceptable for current table size */
- for (uint64 i = 0; i < kCacheBuckets; i++) {
- if (g_buckets[i].mapping == mapping) {
- Page* page = g_buckets[i].page;
- remove_entry(&g_buckets[i]);
- page_decref(page);
- /* After removal, re-check this index since backward shift
- * may have moved an entry into this slot. */
- i--;
- }
- }
-}
-
-Page* page_cache_get_or_alloc(void* mapping, uint64 index)
-{
- /* Fast path: already cached */
- Page* page = page_cache_get(mapping, index);
- if (page) {
- return page;
- }
-
- /* Slow path: allocate and insert */
- page = pmm_alloc_page();
- if (!page) {
- return nullptr;
- }
-
- int ret = page_cache_insert(mapping, index, page);
- if (ret == -EEXIST) {
- /* Race: another thread inserted between our get and insert.
- * Free our allocation and return the existing page. */
- pmm_free_page(page);
- return page_cache_get(mapping, index);
- }
- if (ret < 0) {
- pmm_free_page(page);
- return nullptr;
- }
-
- /* Caller gets a ref (insert already added cache's ref,
- * we need one more for the caller). */
- /* Actually, insert did incref for cache. The page already has
- * refcount=1 from alloc + 1 from insert = 2.
- * We return it to caller who must put() when done.
- * So we incref once more for the caller's reference. */
- page_incref(page);
- return page;
-}
diff --git a/na/mm/pmm.cc b/na/mm/pmm.cc
deleted file mode 100644
index b7cf611..0000000
--- a/na/mm/pmm.cc
+++ /dev/null
@@ -1,528 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * pmm.cc — Physical Memory Manager implementation
- *
- * Buddy allocator with Per-CPU single-page caches (PCP).
- *
- * Initialization sequence (called by BSP in single-core period):
- * 1. memblock + vmm_init allocate and map the page_map at VMEMMAP.
- * 2. Zero and mark all Page descriptors reserved by default.
- * 3. Carve free regions from memblock into the buddy system.
- *
- * The page_map is a flat array indexed by physical page frame number (PFN).
- * PFN = (pa - mem_base) / PAGE_SIZE. This keeps address conversion O(1).
- *
- * Reference: docs/specs/pmm.md
- */
-
-#include <arch/config.h>
-#include <arch/cpu.h>
-#include <kernel/sync/spinlock.h>
-#include <lume/addr.h>
-#include <lume/config.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/memblock.h>
-#include <lume/new.h>
-#include <lume/pmm.h>
-#include <lume/types.h>
-#include <lume/vmm.h>
-
-/* ------------------------------------------------------------------ */
-/* Per-CPU page cache */
-/* ------------------------------------------------------------------ */
-
-struct PerCpuCache {
- list_node free_list; // Single-page free list
- uint32 count; // Number of cached pages
-};
-
-/* ------------------------------------------------------------------ */
-/* Module-private state */
-/* ------------------------------------------------------------------ */
-
-namespace {
-
-/* PCP Cache tuning parameters */
-constexpr uint32 kPcpBatchDivisor = 1024;
-constexpr uint32 kPcpMinBatchSize = 16;
-constexpr uint64 kMB = 1024ULL * 1024ULL;
-
-Spinlock g_pmm_lock;
-list_node g_free_areas[kMaxOrder]; // Buddy free lists [0..10]
-PerCpuCache g_pcp[kMaxCpus];
-
-Page *g_page_map = nullptr; // Flat array of Page descriptors
-uint64 g_page_map_pa = 0; // Physical base of page_map storage
-uint64 g_page_map_bytes = 0; // Byte size of page_map storage
-uint64 g_mem_base = 0; // Physical memory base (from FDT)
-uint64 g_mem_size = 0; // Total physical memory size
-uint64 g_num_pages = 0; // Total managed pages
-
-} // namespace
-
-uint32 g_pcp_high_watermark = 0;
-uint32 g_pcp_batch_size = 0;
-
-/* ------------------------------------------------------------------ */
-/* Address conversion helpers */
-/* ------------------------------------------------------------------ */
-
-/*
- * PFN (Page Frame Number) — fundamental conversion unit.
- * When sparse vmemmap is implemented, only these two functions change.
- * All other PA↔Page conversions go through them.
- */
-uint64 page_to_pfn(const Page *page) {
- uintptr base = reinterpret_cast<uintptr>(g_page_map);
- uintptr addr = reinterpret_cast<uintptr>(page);
- uintptr end = base + g_num_pages * sizeof(Page);
-
- if (addr < base || addr >= end) {
- kernel_panic("page_to_pfn: page outside page_map bounds");
- }
-
- uint64 idx = (addr - base) / sizeof(Page);
- return (g_mem_base / kPageSize) + idx;
-}
-
-Page *pfn_to_page(uint64 pfn) {
- uint64 base_pfn = g_mem_base / kPageSize;
- if (pfn < base_pfn || pfn >= base_pfn + g_num_pages) {
- kernel_panic("pfn_to_page: PFN outside managed range");
- }
- return &g_page_map[pfn - base_pfn];
-}
-
-PhysAddr page_to_pa(const Page *page) {
- return pfn_to_pa(Pfn{page_to_pfn(page)});
-}
-
-Page *pa_to_page(PhysAddr pa) {
- return pfn_to_page(pa_to_pfn(pa).raw);
-}
-
-VirtAddr page_to_va(const Page *page) {
- return pa_to_va(page_to_pa(page));
-}
-
-
-/* ------------------------------------------------------------------ */
-/* Buddy system internals (caller must hold g_pmm_lock) */
-/* ------------------------------------------------------------------ */
-
-namespace {
-
-/*
- * Find a page's buddy at a given order.
- * Buddy PFN = PFN ^ (1 << order).
- */
-inline Page *buddy_of(Page *page, uint8 order) {
- uint64 pfn = static_cast<uint64>(page - g_page_map);
- uint64 buddy_pfn = pfn ^ (1ULL << order);
- if (buddy_pfn >= g_num_pages) {
- return nullptr;
- }
- return &g_page_map[buddy_pfn];
-}
-
-/*
- * Insert a free block into the buddy free list at the given order.
- */
-void buddy_list_add(Page *page, uint8 order) {
- page->type = PageType::Free;
- page->order = order;
- list_node *head = &g_free_areas[order];
- list_node *node = &page->free.free_link;
- node->next = head->next;
- node->prev = head;
- head->next->prev = node;
- head->next = node;
-}
-
-/*
- * Remove a block from whatever buddy free list it is on.
- */
-void buddy_list_del(Page *page) {
- list_node *node = &page->free.free_link;
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = node;
- node->prev = node;
-}
-
-/*
- * Allocate a block of 2^order contiguous pages from the buddy system.
- * Splits higher-order blocks if no exact match is available.
- * Returns nullptr if no memory available.
- */
-Page *buddy_alloc(uint8 order) {
- for (int cur = order; cur < kMaxOrder; cur++) {
- list_node *head = &g_free_areas[cur];
- if (head->next == head) {
- continue; // Empty at this order
- }
-
- // Pop the first block from this order's free list
- list_node *node = head->next;
- // Compute Page* from the free_link member offset
- Page *page =
- reinterpret_cast<Page *>(reinterpret_cast<uintptr>(node) -
- __builtin_offsetof(Page, free.free_link));
- buddy_list_del(page);
-
- // Split down to the requested order
- while (cur > order) {
- cur--;
- // The upper half becomes a free buddy
- Page *split = &g_page_map[(page - g_page_map) + (1ULL << cur)];
- buddy_list_add(split, static_cast<uint8>(cur));
- }
-
- page->type = PageType::Allocated;
- page->order = order;
- page->refcount.store(1, __ATOMIC_RELAXED);
- page->mapcount.store(0, __ATOMIC_RELAXED);
- page->flags.store(0, __ATOMIC_RELAXED);
- page->mapping = nullptr;
- page->index = 0;
- return page;
- }
- return nullptr;
-}
-
-/*
- * Return a block of 2^order pages to the buddy system.
- * Coalesces with its buddy recursively up to kMaxOrder-1.
- */
-void buddy_free(Page *page, uint8 order) {
- uint64 pfn = static_cast<uint64>(page - g_page_map);
-
- while (order < kMaxOrder - 1) {
- Page *buddy = buddy_of(page, order);
- if (!buddy) {
- break;
- }
- // Buddy must be free AND at the same order to coalesce
- if (buddy->type != PageType::Free || buddy->order != order) {
- break;
- }
-
- // Remove buddy from its free list and merge
- buddy_list_del(buddy);
-
- // The merged block starts at the lower PFN
- uint64 buddy_pfn = static_cast<uint64>(buddy - g_page_map);
- if (buddy_pfn < pfn) {
- page = buddy;
- pfn = buddy_pfn;
- }
- order++;
- }
-
- buddy_list_add(page, order);
-}
-
-/* ------------------------------------------------------------------ */
-/* Per-CPU cache operations (interrupts must be disabled by caller) */
-/* ------------------------------------------------------------------ */
-
-/*
- * Refill the PCP from the global buddy system.
- * Acquires g_pmm_lock internally.
- */
-void refill_pcp(PerCpuCache *pcp) {
- LockGuard guard(g_pmm_lock);
- for (uint32 i = 0; i < g_pcp_batch_size; i++) {
- Page *page = buddy_alloc(0);
- if (!page) {
- break;
- }
- // Put onto PCP list (reuse the free_link for PCP chain)
- page->type = PageType::PcpCached;
- list_node *node = &page->free.free_link;
- node->next = pcp->free_list.next;
- node->prev = &pcp->free_list;
- pcp->free_list.next->prev = node;
- pcp->free_list.next = node;
- pcp->count++;
- }
-}
-
-/*
- * Drain half the PCP back to the global buddy system.
- * Acquires g_pmm_lock internally.
- */
-void drain_pcp(PerCpuCache *pcp) {
- LockGuard guard(g_pmm_lock);
- int to_drain = pcp->count / 2;
- for (int i = 0; i < to_drain; i++) {
- list_node *head = &pcp->free_list;
- if (head->next == head) {
- break;
- }
- list_node *node = head->next;
- // Unlink from PCP
- node->prev->next = node->next;
- node->next->prev = node->prev;
- pcp->count--;
-
- Page *page =
- reinterpret_cast<Page *>(reinterpret_cast<uintptr>(node) -
- __builtin_offsetof(Page, free.free_link));
- buddy_free(page, 0);
- }
-}
-
-} // namespace
-
-/* ------------------------------------------------------------------ */
-/* Public API: single-page (fast path via PCP) */
-/* ------------------------------------------------------------------ */
-
-Page *pmm_alloc_page() {
- /*
- * Disable interrupts to prevent reentry on the same CPU
- * (e.g. timer IRQ handler calling pmm_alloc_page while we
- * are mid-way through manipulating the PCP list).
- */
- bool was_on = arch::cpu::intr_enabled();
- arch::cpu::intr_off();
-
- uint64 cpu = arch::cpu::id();
- PerCpuCache *pcp = &g_pcp[cpu];
-
- if (pcp->count == 0) {
- refill_pcp(pcp);
- }
-
- Page *page = nullptr;
- if (pcp->count > 0) {
- list_node *node = pcp->free_list.next;
- // Unlink
- node->prev->next = node->next;
- node->next->prev = node->prev;
- pcp->count--;
-
- page =
- reinterpret_cast<Page *>(reinterpret_cast<uintptr>(node) -
- __builtin_offsetof(Page, free.free_link));
-
- // Validate the page is within the page_map bounds
- if (page < g_page_map || page >= g_page_map + g_num_pages) {
- kernel_panic("pmm_alloc_page: PCP returned out-of-bounds page");
- }
-
- page->type = PageType::Allocated;
- page->order = 0;
- page->refcount.store(1, __ATOMIC_RELAXED);
- page->mapcount.store(0, __ATOMIC_RELAXED);
- page->flags.store(0, __ATOMIC_RELAXED);
- page->mapping = nullptr;
- page->index = 0;
- }
-
- if (was_on) {
- arch::cpu::intr_on();
- }
- return page;
-}
-
-void pmm_free_page(Page *page) {
- if (!page) {
- kernel_panic("pmm_free_page: null page");
- }
-
- bool was_on = arch::cpu::intr_enabled();
- arch::cpu::intr_off();
-
- uint64 cpu = arch::cpu::id();
- PerCpuCache *pcp = &g_pcp[cpu];
-
- // Push onto PCP
- page->type = PageType::PcpCached;
- page->order = 0;
- list_node *node = &page->free.free_link;
- node->next = pcp->free_list.next;
- node->prev = &pcp->free_list;
- pcp->free_list.next->prev = node;
- pcp->free_list.next = node;
- pcp->count++;
-
- // Drain if over high watermark
- if (pcp->count >= g_pcp_high_watermark) {
- drain_pcp(pcp);
- }
-
- if (was_on) {
- arch::cpu::intr_on();
- }
-}
-
-/* ------------------------------------------------------------------ */
-/* Public API: multi-page (slow path, direct buddy) */
-/* ------------------------------------------------------------------ */
-
-Page *pmm_alloc_pages(uint8 order) {
- if (order >= kMaxOrder) {
- return nullptr;
- }
- LockGuard guard(g_pmm_lock);
- return buddy_alloc(order);
-}
-
-void pmm_free_pages(Page *page, uint8 order) {
- if (!page) {
- kernel_panic("pmm_free_pages: null page");
- }
- if (order >= kMaxOrder) {
- kernel_panic("pmm_free_pages: order out of range");
- }
- LockGuard guard(g_pmm_lock);
- buddy_free(page, order);
-}
-
-/* ------------------------------------------------------------------ */
-/* Public API: reference counting */
-/* ------------------------------------------------------------------ */
-
-void page_incref(Page *page) { page->refcount.fetch_add(1); }
-
-void page_decref(Page *page) {
- uint32 old = page->refcount.fetch_sub(1);
- if (old == 1) {
- // Refcount dropped to 0 — return to pool based on originally allocated
- // order
- if (page->order == 0) {
- pmm_free_page(page);
- } else {
- pmm_free_pages(page, page->order);
- }
- }
-}
-
-/* ------------------------------------------------------------------ */
-/* Initialization */
-/* ------------------------------------------------------------------ */
-
-/*
- * Helper: zero out a range of bytes. We cannot use memset here
- * because lib/string may not be linked yet.
- */
-namespace {
-void zero_range(void *start, uint64 bytes) {
- uint8 *p = static_cast<uint8 *>(start);
- for (uint64 i = 0; i < bytes; i++) {
- p[i] = 0;
- }
-}
-} // namespace
-
-void pmm_init() {
- /* 1. Get memory layout from memblock (already scanned FDT) */
- g_mem_base = memblock_get_mem_base();
- g_mem_size = memblock_get_mem_size();
- g_num_pages = vmm_get_num_pages();
-
- if (g_mem_size == 0) {
- kernel_panic("pmm_init: memblock reports zero memory");
- }
-
- /* 2. PCP parameters */
- uint32 calc_batch = static_cast<uint32>(g_num_pages / (kMaxCpus * kPcpBatchDivisor));
- g_pcp_batch_size = (calc_batch > kPcpMinBatchSize) ? calc_batch : kPcpMinBatchSize;
- g_pcp_high_watermark = g_pcp_batch_size * 4;
-
- /* 3. page_map was allocated by vmm_init at vmemmap VA.
- * No relocation needed — page_map starts life at the right VA. */
- g_page_map_pa = vmm_get_page_map_pa();
- g_page_map_bytes = vmm_get_page_map_size();
- g_page_map = reinterpret_cast<Page *>(arch::g_vmemmap_base);
-
- /* 4. Zero out the entire page_map */
- zero_range(g_page_map, g_page_map_bytes);
-
- /* 4b. Default every page descriptor to reserved/non-free. */
- for (uint64 i = 0; i < g_num_pages; i++) {
- Page *page = &g_page_map[i];
- page->refcount.store(0, __ATOMIC_RELAXED);
- page->mapcount.store(0, __ATOMIC_RELAXED);
- page->flags.store(0, __ATOMIC_RELAXED);
- page->type = PageType::Allocated;
- page->order = 0;
- page->mapping = nullptr;
- page->index = 0;
- page->lru.init();
- page->free.free_link.init();
- }
-
- /* 5. Initialize buddy free list heads */
- g_pmm_lock.init("pmm");
- for (int i = 0; i < kMaxOrder; i++) {
- g_free_areas[i].init();
- }
-
- /* 6. Initialize PCP list heads */
- for (int i = 0; i < kMaxCpus; i++) {
- g_pcp[i].free_list.init();
- g_pcp[i].count = 0;
- }
-
- /* 7. Feed free regions from memblock into the buddy system.
- * memblock knows about all reserved regions (firmware, kernel,
- * page table pages, page_map). Everything else is free. */
- struct FeedCtx {
- uint64 total_pages;
- };
- FeedCtx ctx{0};
-
- memblock_for_each_free(
- [](uint64 base, uint64 size, void *arg) {
- auto *c = static_cast<FeedCtx *>(arg);
- uint64 start_pfn = (base - memblock_get_mem_base()) / kPageSize;
- uint64 end_pfn = start_pfn + size / kPageSize;
-
- /* Access page_map via the globals directly (they are in anon namespace now) */
-
- uint64 pfn = start_pfn;
- while (pfn < end_pfn) {
- uint8 order = 0;
- for (int o = kMaxOrder - 1; o >= 0; o--) {
- uint64 block_size = 1ULL << o;
- if ((pfn & (block_size - 1)) == 0 && pfn + block_size <= end_pfn) {
- order = static_cast<uint8>(o);
- break;
- }
- }
-
- Page *page = &g_page_map[pfn];
- page->type = PageType::Free;
- page->order = order;
- page->refcount.store(0, __ATOMIC_RELAXED);
- page->free.free_link.init();
-
- /* Inline buddy_list_add since we're inside a lambda */
- list_node *head = &g_free_areas[order];
- list_node *node = &page->free.free_link;
- node->next = head->next;
- node->prev = head;
- head->next->prev = node;
- head->next = node;
-
- pfn += (1ULL << order);
- c->total_pages += (1ULL << order);
- }
- },
- &ctx);
-
- kprintf("[pmm] buddy: %llu free pages (%llu MB)\n", ctx.total_pages,
- (ctx.total_pages * kPageSize) / kMB);
-
- /* 8. Retire memblock — all future allocations go through PMM */
- memblock_retire();
-}
-
-PhysAddr pmm_get_mem_base() { return phys_addr(g_mem_base); }
-uint64 pmm_get_mem_size() { return g_mem_size; }
diff --git a/na/mm/slab.cc b/na/mm/slab.cc
deleted file mode 100644
index 43d8c4a..0000000
--- a/na/mm/slab.cc
+++ /dev/null
@@ -1,321 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * slab.cc — SLUB allocator implementation
- *
- * Key design points:
- * - Free object list is embedded in the objects themselves (first 8 bytes).
- * - Per-page metadata lives in Page.slab (cache, obj_count, freelist).
- * - Per-CPU "active page" avoids lock contention on the fast path.
- * - partial_ list protected by per-KmemCache Spinlock.
- * - PMM calls happen outside the lock to avoid lock ordering issues.
- *
- * Address note: the kernel may be running at physical addresses via
- * identity mapping (before vmm_init). We handle PA/VA carefully:
- * PMM gives us Page*, page_to_pa() gives PA, and we access page
- * contents via pa_to_va() (higher-half VA, always mapped).
- *
- * Reference: docs/specs/slab.md
- */
-
-#include "slab.h"
-#include <lume/pmm.h>
-#include <lume/addr.h>
-#include <lume/klog.h>
-#include <lume/new.h>
-#include <arch/cpu.h>
-
-/* ------------------------------------------------------------------ */
-/* Helpers */
-/* ------------------------------------------------------------------ */
-
-/*
- * Convert a Page* to the usable virtual address of the page's contents.
- * Uses the higher-half mapping which is always active.
- */
-static inline void* page_to_data_va(Page* page)
-{
- return reinterpret_cast<void*>(page_to_va(page).raw);
-}
-
-/* Round up x to the next multiple of align (align must be power of 2) */
-static inline uint32 align_up(uint32 x, uint32 align)
-{
- return (x + align - 1) & ~(align - 1);
-}
-
-/* ------------------------------------------------------------------ */
-/* Global size-class caches */
-/* ------------------------------------------------------------------ */
-
-KmemCache g_size_caches[kNumSizeClasses];
-
-/* ------------------------------------------------------------------ */
-/* KmemCache::init */
-/* ------------------------------------------------------------------ */
-
-void KmemCache::init(const char* name, uint32 obj_size, uint32 obj_align)
-{
- name_ = name;
- obj_align_ = (obj_align < 8) ? 8 : obj_align; // minimum 8 for freelist ptr
- obj_size_ = align_up((obj_size < 8) ? 8 : obj_size, obj_align_);
- objs_per_slab_ = kPageSize / obj_size_;
-
- lock_.init(name);
- partial_.init();
-
- for (int i = 0; i < kMaxCpus; i++)
- cpu_slab_[i].active = nullptr;
-}
-
-/* ------------------------------------------------------------------ */
-/* KmemCache::new_slab — allocate and format a fresh slab page */
-/* ------------------------------------------------------------------ */
-
-Page* KmemCache::new_slab()
-{
- Page* page = pmm_alloc_page();
- if (!page)
- return nullptr;
-
- page->type = PageType::Slab;
- page->slab.cache = this;
- page->slab.obj_count = 0;
-
- /* Build the embedded free list across all object slots */
- uint8* base = static_cast<uint8*>(page_to_data_va(page));
- void* head = nullptr;
-
- /* Chain objects from last to first so freelist order = low addr first */
- for (int i = static_cast<int>(objs_per_slab_) - 1; i >= 0; i--) {
- void* obj = base + i * obj_size_;
- *reinterpret_cast<void**>(obj) = head;
- head = obj;
- }
- page->slab.freelist = head;
-
- return page;
-}
-
-/* ------------------------------------------------------------------ */
-/* Partial list management (lock must be held) */
-/* ------------------------------------------------------------------ */
-
-Page* KmemCache::get_partial()
-{
- /*
- * Phase 1 simplification: no partial list tracking.
- * The slow path always allocates a fresh page from PMM.
- * Full pages stay alive (Page.slab.cache is valid for kfree).
- * Empty pages are returned to PMM immediately.
- *
- * TODO: add list_node to Page.slab and implement partial tracking
- * to avoid wasting partially-filled pages.
- */
- (void)partial_;
- return nullptr;
-}
-
-void KmemCache::put_partial(Page* page)
-{
- (void)page;
-}
-
-void KmemCache::remove_partial(Page* page)
-{
- (void)page;
-}
-
-/* ------------------------------------------------------------------ */
-/* KmemCache::alloc */
-/* ------------------------------------------------------------------ */
-
-void* KmemCache::alloc()
-{
- bool was_on = arch::cpu::intr_enabled();
- arch::cpu::intr_off();
-
- uint64 cpu = arch::cpu::id();
- Page* active = cpu_slab_[cpu].active;
-
- /* Fast path: pop from active page's freelist */
- if (active && active->slab.freelist) {
- void* obj = active->slab.freelist;
- active->slab.freelist = *reinterpret_cast<void**>(obj);
- active->slab.obj_count++;
- if (was_on) arch::cpu::intr_on();
- return obj;
- }
-
- /* Slow path: need a new active page */
-
- /* If current active is full, detach it (it stays as Slab state,
- * cache pointer still valid for kfree to find it) */
- if (active && !active->slab.freelist) {
- cpu_slab_[cpu].active = nullptr;
- }
-
- if (was_on) arch::cpu::intr_on();
-
- /* Try to get a partial page or allocate a fresh one.
- * PMM call happens without any lock held. */
- Page* new_page = new_slab();
- if (!new_page)
- return nullptr;
-
- /* Install as new active page */
- was_on = arch::cpu::intr_enabled();
- arch::cpu::intr_off();
-
- /* Another interrupt handler might have set a new active while we
- * were allocating. If so, put ours aside (simplified: just use ours). */
- cpu_slab_[cpu].active = new_page;
-
- void* obj = new_page->slab.freelist;
- new_page->slab.freelist = *reinterpret_cast<void**>(obj);
- new_page->slab.obj_count++;
-
- if (was_on) arch::cpu::intr_on();
- return obj;
-}
-
-/* ------------------------------------------------------------------ */
-/* KmemCache::free */
-/* ------------------------------------------------------------------ */
-
-void KmemCache::free(void* obj)
-{
- if (!obj)
- return;
-
- /* Find the Page for this object's page */
- uint64 obj_addr = reinterpret_cast<uint64>(obj);
-
- /* Convert VA to PA. Like pmm.cc, handle both PA and VA cases. */
- uint64 obj_pa = va_to_pa(virt_addr(obj_addr)).raw;
- uint64 page_pa = obj_pa & ~(kPageSize - 1);
- Page* page = pa_to_page(phys_addr(page_pa));
-
- if (page->type != PageType::Slab || page->slab.cache != this)
- kernel_panic("slab free: object does not belong to this cache");
-
- bool was_on = arch::cpu::intr_enabled();
- arch::cpu::intr_off();
-
- /* Push object back onto the page's freelist */
- *reinterpret_cast<void**>(obj) = page->slab.freelist;
- page->slab.freelist = obj;
- page->slab.obj_count--;
-
- /* If page is now completely empty, return it to PMM */
- if (page->slab.obj_count == 0) {
- /* If this page is our active, clear it */
- uint64 cpu = arch::cpu::id();
- if (cpu_slab_[cpu].active == page)
- cpu_slab_[cpu].active = nullptr;
-
- if (was_on) arch::cpu::intr_on();
-
- /* Return page to PMM */
- page->slab.cache = nullptr;
- page->slab.freelist = nullptr;
- pmm_free_page(page);
- return;
- }
-
- if (was_on) arch::cpu::intr_on();
-}
-
-/* ------------------------------------------------------------------ */
-/* Public free-function API */
-/* ------------------------------------------------------------------ */
-
-/* Find the smallest size class >= size */
-static KmemCache* find_cache(uint32 size)
-{
- for (int i = 0; i < kNumSizeClasses; i++) {
- if (kSizeClasses[i] >= size)
- return &g_size_caches[i];
- }
- return nullptr; // Too large for slab
-}
-
-void* kmalloc(uint32 size)
-{
- if (size == 0)
- return nullptr;
-
- /* Large allocation: fall back to PMM directly */
- if (size > kSizeClasses[kNumSizeClasses - 1]) {
- uint8 order = 0;
- uint64 needed = (size + kPageSize - 1) / kPageSize;
- while ((1ULL << order) < needed)
- order++;
- Page* page = pmm_alloc_pages(order);
- if (!page)
- return nullptr;
- return reinterpret_cast<void*>(pa_to_va(page_to_pa(page)).raw);
- }
-
- KmemCache* cache = find_cache(size);
- if (!cache)
- return nullptr;
- return cache->alloc();
-}
-
-void kfree(void* ptr)
-{
- if (!ptr)
- return;
-
- uint64 addr = reinterpret_cast<uint64>(ptr);
- uint64 pa = va_to_pa(virt_addr(addr)).raw;
- uint64 page_pa = pa & ~(kPageSize - 1);
- Page* page = pa_to_page(phys_addr(page_pa));
-
- if (page->type == PageType::Slab) {
- KmemCache* cache = page->slab.cache;
- if (!cache)
- kernel_panic("kfree: slab page has null cache");
- cache->free(ptr);
- } else if (page->type == PageType::Allocated) {
- /* Large allocation — return directly to PMM */
- pmm_free_pages(page, page->order);
- } else {
- kernel_panic("kfree: invalid page state");
- }
-}
-
-/* ------------------------------------------------------------------ */
-/* C++ operator new / delete */
-/* ------------------------------------------------------------------ */
-
-/* Use decltype(sizeof(0)) instead of size_t to match the compiler's
- * built-in expectation for operator new (unsigned long vs unsigned long long). */
-using __sz = decltype(sizeof(0));
-
-void* operator new(__sz size) { return kmalloc(static_cast<uint32>(size)); }
-void* operator new[](__sz size) { return kmalloc(static_cast<uint32>(size)); }
-
-void operator delete(void* ptr) noexcept { kfree(ptr); }
-void operator delete[](void* ptr) noexcept { kfree(ptr); }
-void operator delete(void* ptr, __sz) noexcept { kfree(ptr); }
-void operator delete[](void* ptr, __sz) noexcept { kfree(ptr); }
-
-/* ------------------------------------------------------------------ */
-/* Initialization */
-/* ------------------------------------------------------------------ */
-
-void slab_init()
-{
- for (int i = 0; i < kNumSizeClasses; i++) {
- /* Build a name string like "kmalloc-32" */
- static const char* names[kNumSizeClasses] = {
- "kmalloc-8", "kmalloc-16", "kmalloc-32",
- "kmalloc-64", "kmalloc-128", "kmalloc-256",
- "kmalloc-512", "kmalloc-1024","kmalloc-2048"
- };
- g_size_caches[i].init(names[i], kSizeClasses[i], 8);
- }
-}
diff --git a/na/mm/slab.h b/na/mm/slab.h
deleted file mode 100644
index 909ee62..0000000
--- a/na/mm/slab.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#pragma once
-
-/*
- * SLUB Allocator — module-private header
- *
- * SLUB = Simple List of Unfull Blocks.
- * Metadata lives in Page descriptors (no per-page Slab header).
- * Free object list is embedded inside the objects themselves.
- * Per-CPU "active page" eliminates lock contention on the fast path.
- *
- * Reference: docs/specs/slab.md
- */
-
-#include <lume/types.h>
-#include <lume/config.h>
-#include <lume/page.h>
-#include <lume/list.h>
-#include <kernel/sync/spinlock.h>
-
-/* Size classes: 8, 16, 32, 64, 128, 256, 512, 1024, 2048 */
-inline constexpr int kNumSizeClasses = 9;
-inline constexpr uint32 kSizeClasses[kNumSizeClasses] = {
- 8, 16, 32, 64, 128, 256, 512, 1024, 2048
-};
-
-/* Per-CPU active slab page pointer */
-struct PerCpuSlab {
- Page* active; // Currently used slab page (nullptr if none)
-};
-
-/*
- * KmemCache — manages a pool of fixed-size objects.
- *
- * Lock contract:
- * lock_ protects partial_ list only.
- * Per-CPU cpu_slab_ access requires interrupts disabled.
- * PMM calls happen outside lock_ to avoid lock ordering issues.
- */
-class KmemCache {
-public:
- KmemCache() = default;
-
- // Explicit init (Placement New / BSS pattern)
- void init(const char* name, uint32 obj_size, uint32 obj_align);
-
- // Allocate one object. Returns nullptr on OOM.
- void* alloc();
-
- // Free one object back to its owning page.
- void free(void* obj);
-
- const char* name() const { return name_; }
- uint32 obj_size() const { return obj_size_; }
-
-private:
- // Get a partially-filled page from the partial list (lock must be held)
- Page* get_partial();
-
- // Put a page onto the partial list (lock must be held)
- void put_partial(Page* page);
-
- // Remove a page from the partial list (lock must be held)
- void remove_partial(Page* page);
-
- // Allocate a new page from PMM and format it as a slab
- Page* new_slab();
-
- Spinlock lock_; // Protects partial_ only
- list_node partial_; // Partially-filled slab pages
-
- PerCpuSlab cpu_slab_[kMaxCpus]; // Per-CPU active page
-
- const char* name_ = "uninit";
- uint32 obj_size_ = 0; // Aligned object size (bytes)
- uint32 obj_align_ = 0; // Alignment requirement
- uint32 objs_per_slab_ = 0; // Objects per 4KB page
-};
-
-/* Global size-class caches */
-extern KmemCache g_size_caches[kNumSizeClasses];
diff --git a/na/mm/tlb.cc b/na/mm/tlb.cc
deleted file mode 100644
index b7a8ba7..0000000
--- a/na/mm/tlb.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * tlb.cc — TLB shootdown implementation
- *
- * Uses RISC-V SBI Remote Fence extension to perform cross-hart
- * TLB invalidation. SBI firmware handles the IPI delivery and
- * synchronization — the call blocks until all target harts have
- * completed their local sfence.vma.
- *
- * When a VmSpace is provided, we use its cpu_mask to target only
- * the CPUs that have this address space loaded in satp. This avoids
- * unnecessary IPI storms on unrelated cores.
- *
- * Reference: docs/specs/linux_abi_mm_plan.md §SMP §2.1
- */
-
-#include <lume/tlb.h>
-#include <lume/vmm.h>
-#include <lume/config.h>
-#include <arch/mmu.h>
-#include <arch/tlb.h>
-#include <arch/cpu.h>
-
-/*
- * If a shootdown range exceeds this many pages, do a full flush
- * instead of per-page invalidation.
- */
-static constexpr uint64 kShootdownPageThreshold = 32;
-
-/*
- * Build a cpu mask for all online CPUs (fallback when mm is null).
- */
-namespace {
-uint64 all_cpus_mask()
-{
- uint64 mask = 0;
- for (uint32 i = 0; i < kMaxCpus; i++) {
- mask |= (1ULL << i);
- }
- return mask;
-}
-
-/*
- * Get the target cpu mask: use mm->cpu_mask when available,
- * fall back to all_cpus_mask for kernel address space (mm == nullptr).
- */
-uint64 target_cpu_mask(VmSpace* mm)
-{
- if (mm) {
- uint64 mask = mm->cpu_mask.load(__ATOMIC_RELAXED);
- /* Always include our own CPU — we may still have stale TLB */
- mask |= (1ULL << arch::cpu::id());
- return mask;
- }
- return all_cpus_mask();
-}
-} // namespace
-
-void tlb_shootdown_range(VmSpace* mm, uint64 start, uint64 end)
-{
- if (start >= end) {
- return;
- }
-
- uint64 num_pages = (end - start) / kPageSize;
- uint64 cpu_mask = target_cpu_mask(mm);
-
- if (num_pages > kShootdownPageThreshold) {
- /* Full flush on target CPUs via arch backend */
- arch::tlb::flush_remote(cpu_mask, 0, ~0ULL);
- return;
- }
-
- /* Range flush on target CPUs via arch backend */
- arch::tlb::flush_remote(cpu_mask, start, end - start);
-}
-
-void tlb_shootdown_page(VmSpace* mm, uint64 va)
-{
- uint64 cpu_mask = target_cpu_mask(mm);
- arch::tlb::flush_remote(cpu_mask, va, kPageSize);
-}
-
-void tlb_shootdown_all()
-{
- uint64 cpu_mask = all_cpus_mask();
- arch::tlb::flush_remote(cpu_mask, 0, ~0ULL);
-}
diff --git a/na/mm/vma.cc b/na/mm/vma.cc
deleted file mode 100644
index da89e08..0000000
--- a/na/mm/vma.cc
+++ /dev/null
@@ -1,640 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * vma.cc — Virtual Memory Area management (MI layer)
- *
- * Manages user address spaces using a red-black tree of VMA regions.
- * Implements demand paging: vmm_map_user records intent only;
- * physical pages are allocated on page fault via vmm_handle_page_fault.
- *
- * MI/MD separation: this file NEVER references pmap::PTE_* constants.
- * Permission translation is delegated to pmap::vm_perm_to_pte().
- *
- * Reference: docs/specs/vmm.md
- */
-
-#include <lume/vmm.h>
-#include <lume/addr.h>
-#include <lume/config.h>
-#include <lume/errno.h>
-#include <lume/pmm.h>
-#include <lume/new.h>
-#include <lume/klog.h>
-#include <arch/pmap.h>
-#include <lume/tlb.h>
-#include <lume/mm_layout.h>
-
-/* ------------------------------------------------------------------ */
-/* Rbtree callbacks for VMA interval tree */
-/* ------------------------------------------------------------------ */
-
-static bool vma_less(struct rb_node* node, const struct rb_node* parent) {
- VmAreaStruct* n = reinterpret_cast<VmAreaStruct*>(
- reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb));
- const VmAreaStruct* p = reinterpret_cast<const VmAreaStruct*>(
- reinterpret_cast<const char*>(parent) - __builtin_offsetof(VmAreaStruct, rb));
- return n->start < p->start;
-}
-
-static int vma_cmp(const void* key, const struct rb_node* node) {
- uint64 addr = *static_cast<const uint64*>(key);
- const VmAreaStruct* n = reinterpret_cast<const VmAreaStruct*>(
- reinterpret_cast<const char*>(node) - __builtin_offsetof(VmAreaStruct, rb));
- if (addr < n->start) return -1;
- if (addr >= n->end) return 1;
- return 0;
-}
-
-static VmAreaStruct* node_to_vma(struct rb_node* node)
-{
- return reinterpret_cast<VmAreaStruct*>(
- reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb));
-}
-
-static uint64 align_up_any(uint64 value, uint64 align)
-{
- if (align == 0) {
- return value;
- }
- return ((value + align - 1) / align) * align;
-}
-
-static bool range_overlaps(uint64 a_start, uint64 a_end,
- uint64 b_start, uint64 b_end)
-{
- return a_start < b_end && b_start < a_end;
-}
-
-
-
-static bool vma_overlaps_locked(VmSpace* space, uint64 start, uint64 end)
-{
- struct rb_node* node = rb_first(&space->vma_tree);
- while (node) {
- VmAreaStruct* vma = node_to_vma(node);
- if (range_overlaps(start, end, vma->start, vma->end)) {
- return true;
- }
- node = rb_next(node);
- }
- return false;
-}
-
-static bool vma_range_covered_locked(VmSpace* space, uint64 start, uint64 end)
-{
- uint64 cursor = start;
- struct rb_node* node = rb_first(&space->vma_tree);
- while (node && cursor < end) {
- VmAreaStruct* vma = node_to_vma(node);
- if (vma->end <= cursor) {
- node = rb_next(node);
- continue;
- }
- if (vma->start > cursor) {
- return false;
- }
- if (vma->end > cursor) {
- cursor = vma->end;
- }
- node = rb_next(node);
- }
- return cursor >= end;
-}
-
-static void vma_try_merge_neighbors_locked(VmSpace* space, VmAreaStruct* vma)
-{
- struct rb_node* prev_node = rb_prev(&vma->rb);
- if (prev_node) {
- VmAreaStruct* prev = node_to_vma(prev_node);
- if (prev->end == vma->start && prev->perm == vma->perm) {
- prev->end = vma->end;
- rb_erase(&vma->rb, &space->vma_tree);
- delete vma;
- vma = prev;
- }
- }
-
- struct rb_node* next_node = rb_next(&vma->rb);
- if (next_node) {
- VmAreaStruct* next = node_to_vma(next_node);
- if (vma->end == next->start && vma->perm == next->perm) {
- vma->end = next->end;
- rb_erase(&next->rb, &space->vma_tree);
- delete next;
- }
- }
-}
-
-static int vma_split_locked(VmSpace* space, VmAreaStruct* vma, uint64 split)
-{
- if (split <= vma->start || split >= vma->end) {
- return 0;
- }
-
- VmAreaStruct* right = new VmAreaStruct;
- if (!right) {
- return -ENOMEM;
- }
-
- right->start = split;
- right->end = vma->end;
- right->perm = vma->perm;
- right->mm = space;
-
- vma->end = split;
- rb_add(&right->rb, &space->vma_tree, vma_less);
- return 0;
-}
-
-static int vma_split_range_locked(VmSpace* space, uint64 start, uint64 end)
-{
- if (end <= start) {
- return 0;
- }
-
- struct rb_node* start_node = rb_find(&start, &space->vma_tree, vma_cmp);
- if (!start_node) {
- return -ENOMEM;
- }
- VmAreaStruct* start_vma = node_to_vma(start_node);
- int ret = vma_split_locked(space, start_vma, start);
- if (ret < 0) {
- return ret;
- }
-
- uint64 end_key = end - 1;
- struct rb_node* end_node = rb_find(&end_key, &space->vma_tree, vma_cmp);
- if (!end_node) {
- return -ENOMEM;
- }
- VmAreaStruct* end_vma = node_to_vma(end_node);
- return vma_split_locked(space, end_vma, end);
-}
-
-/* ------------------------------------------------------------------ */
-/* VmSpace lifecycle */
-/* ------------------------------------------------------------------ */
-
-VmSpace* VmSpace::create() {
- VmSpace* space = new VmSpace;
- if (!space) return nullptr;
-
- space->root_pa = pmap::create();
- if (!space->root_pa) {
- delete space;
- return nullptr;
- }
-
- space->vma_tree = RB_ROOT;
- space->lock.init("VmSpace");
- space->refcount.store(1, __ATOMIC_RELAXED);
- space->brk_start = 0;
- space->brk_end = 0;
- return space;
-}
-
-void VmSpace::incref() {
- refcount.fetch_add(1, __ATOMIC_RELAXED);
-}
-
-void VmSpace::put() {
- if (refcount.fetch_sub(1, __ATOMIC_ACQ_REL) == 1) {
- destroy();
- }
-}
-
-void VmSpace::destroy() {
- /* Drain VMA tree under lock, then release BEFORE delete this */
- lock.acquire();
- uint64 root = root_pa;
- struct rb_node* node = rb_first(&vma_tree);
- while (node) {
- VmAreaStruct* vma = reinterpret_cast<VmAreaStruct*>(
- reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb));
-
- /* Decref and dec mapcount for all mapped physical pages */
- for (uint64 addr = vma->start; addr < vma->end; addr += kPageSize) {
- uint64 pa;
- if (pmap::lookup(root, addr, &pa)) {
- Page* pg = pa_to_page(phys_addr(pa));
- page_dec_mapcount(pg);
- page_decref(pg);
- }
- }
-
- node = rb_next(node);
- rb_erase(&vma->rb, &vma_tree);
- delete vma;
- }
- lock.release();
-
- /* Destroy hardware page tables outside the lock */
- pmap::destroy(root);
-
- /* Finally free VmSpace itself */
- delete this;
-}
-
-/* ------------------------------------------------------------------ */
-/* Public API: VMA manipulation */
-/* ------------------------------------------------------------------ */
-
-int vmm_map_user(VmSpace* space, uint64 va, uint64 len, uint64 perm) {
- if (!space) return -EINVAL;
-
- va = page_align_down(va);
- len = page_align_up(len);
- if (len == 0) return 0;
- if (va < kUserVaBase) return -EINVAL;
- if (va >= kUserVaLimit) return -EINVAL;
- if (va + len < va || va + len > kUserVaLimit) return -EINVAL;
-
- VmAreaStruct* vma = new VmAreaStruct;
- if (!vma) return -ENOMEM;
- vma->start = va;
- vma->end = va + len;
- vma->perm = perm | VM_USER; // Enforce user-mode access (MI flag)
- vma->mm = space;
-
- LockGuard guard(space->lock);
- if (vma_overlaps_locked(space, vma->start, vma->end)) {
- delete vma;
- return -EINVAL;
- }
- rb_add(&vma->rb, &space->vma_tree, vma_less);
- vma_try_merge_neighbors_locked(space, vma);
- return 0;
-}
-
-int vmm_unmap_user(VmSpace* space, uint64 va, uint64 len) {
- if (!space) return -EINVAL;
- va = page_align_down(va);
- len = page_align_up(len);
- if (len == 0) return 0;
- if (va < kUserVaBase) return -EINVAL;
- if (va >= kUserVaLimit) return -EINVAL;
- if (va + len < va || va + len > kUserVaLimit) return -EINVAL;
- uint64 end = va + len;
-
- LockGuard guard(space->lock);
- struct rb_node* node = rb_first(&space->vma_tree);
- while (node) {
- VmAreaStruct* vma = node_to_vma(node);
- struct rb_node* next = rb_next(node);
-
- if (vma->end <= va) {
- node = next;
- continue;
- }
- if (vma->start >= end) {
- break;
- }
-
- uint64 unmap_start = (vma->start > va) ? vma->start : va;
- uint64 unmap_end = (vma->end < end) ? vma->end : end;
-
- if (unmap_start < unmap_end) {
- for (uint64 addr = unmap_start; addr < unmap_end; addr += kPageSize) {
- uint64 pa;
- if (pmap::lookup(space->root_pa, addr, &pa)) {
- pmap::unmap(space->root_pa, addr);
- Page* pg = pa_to_page(phys_addr(pa));
- page_dec_mapcount(pg);
- page_decref(pg);
- }
- }
- }
-
- if (unmap_start == vma->start && unmap_end == vma->end) {
- rb_erase(&vma->rb, &space->vma_tree);
- delete vma;
- } else if (unmap_start == vma->start) {
- rb_erase(&vma->rb, &space->vma_tree);
- vma->start = unmap_end;
- rb_add(&vma->rb, &space->vma_tree, vma_less);
- } else if (unmap_end == vma->end) {
- vma->end = unmap_start;
- } else {
- VmAreaStruct* right = new VmAreaStruct;
- if (!right) {
- return -ENOMEM;
- }
- right->start = unmap_end;
- right->end = vma->end;
- right->perm = vma->perm;
- right->mm = space;
-
- vma->end = unmap_start;
- rb_add(&right->rb, &space->vma_tree, vma_less);
- break;
- }
-
- node = next;
- }
- return 0;
-}
-
-int vmm_protect_user(VmSpace* space, uint64 va, uint64 len, uint64 perm)
-{
- if (!space) return -EINVAL;
- va = page_align_down(va);
- len = page_align_up(len);
- if (len == 0) return -EINVAL;
- if (va < kUserVaBase) return -EINVAL;
- if (va >= kUserVaLimit) return -EINVAL;
- if (va + len < va || va + len > kUserVaLimit) return -EINVAL;
-
- uint64 end = va + len;
-
- LockGuard guard(space->lock);
- if (!vma_range_covered_locked(space, va, end)) {
- return -ENOMEM;
- }
- int ret = vma_split_range_locked(space, va, end);
- if (ret < 0) {
- return ret;
- }
-
- bool tlb_flush_needed = false;
- struct rb_node* node = rb_find(&va, &space->vma_tree, vma_cmp);
- while (node) {
- VmAreaStruct* vma = node_to_vma(node);
- if (vma->start >= end) {
- break;
- }
- if (vma->end <= va) {
- node = rb_next(node);
- continue;
- }
-
- vma->perm = (perm | VM_USER);
-
- for (uint64 addr = vma->start; addr < vma->end; addr += kPageSize) {
- uint64 pa;
- if (!pmap::lookup(space->root_pa, addr, &pa)) {
- continue;
- }
-
- if (perm == 0) {
- Page* pg = pa_to_page(phys_addr(pa));
- page_dec_mapcount(pg);
- page_decref(pg);
- pmap::unmap(space->root_pa, addr);
- tlb_flush_needed = true;
- continue;
- }
-
- uint64 pte_perm = pmap::vm_perm_to_pte(vma->perm);
- if ((vma->perm & VM_WRITE) && pmap::is_cow(space->root_pa, addr)) {
- pte_perm &= ~pmap::PTE_W;
- pte_perm |= pmap::PTE_COW;
- }
-
- int ret = pmap::protect(space->root_pa, addr, pte_perm);
- if (ret < 0) {
- return ret;
- }
- tlb_flush_needed = true;
- }
-
- node = rb_next(node);
- }
-
- if (tlb_flush_needed) {
- tlb_shootdown_range(space, va, end);
- }
- return 0;
-}
-
-uint64 mm_find_free_va(VmSpace* space, uint64 length, uint64 align)
-{
- if (!space) return 0;
- length = page_align_up(length);
- if (length == 0) return 0;
- if (align < kPageSize) align = kPageSize;
-
- LockGuard guard(space->lock);
-
- /* Linux: arch_get_unmapped_area() starts from mm->mmap_base.
- * RISC-V: TASK_UNMAPPED_BASE = PAGE_ALIGN(TASK_SIZE / 3).
- * Fall back to kUserVaBase if mmap_base not set (kernel VmSpaces). */
- uint64 base = space->mmap_base ? space->mmap_base : kUserVaBase;
- uint64 cursor = align_up_any(base, align);
- struct rb_node* node = rb_first(&space->vma_tree);
- while (node) {
- VmAreaStruct* vma = node_to_vma(node);
- if (cursor <= vma->start) {
- if (cursor > kUserVaLimit - length) {
- return 0;
- }
- if (cursor + length <= vma->start) {
- return cursor;
- }
- }
- if (cursor < vma->end) {
- cursor = vma->end;
- }
- cursor = align_up_any(cursor, align);
- node = rb_next(node);
- }
-
- if (cursor > kUserVaLimit - length) {
- return 0;
- }
- return cursor;
-}
-
-bool vma_range_covered(VmSpace* space, uint64 start, uint64 end)
-{
- if (!space) return false;
- start = page_align_down(start);
- end = page_align_up(end);
- if (end <= start) return true;
-
- LockGuard guard(space->lock);
- return vma_range_covered_locked(space, start, end);
-}
-
-/* ------------------------------------------------------------------ */
-/* Page fault handler (demand paging) */
-/* ------------------------------------------------------------------ */
-
-static bool vma_allows(const VmAreaStruct* vma, VmFaultCause cause)
-{
- switch (cause) {
- case VmFaultCause::Read:
- return (vma->perm & VM_READ) != 0;
- case VmFaultCause::Write:
- return (vma->perm & VM_WRITE) != 0;
- case VmFaultCause::Exec:
- return (vma->perm & VM_EXEC) != 0;
- default:
- return false;
- }
-}
-
-int vmm_handle_page_fault(VmSpace* space, uint64 fault_addr, VmFaultCause cause) {
- if (!space) return -EINVAL;
-
- uint64 aligned_addr = page_align_down(fault_addr);
-
- LockGuard guard(space->lock);
- struct rb_node* node = rb_find(&fault_addr, &space->vma_tree, vma_cmp);
- if (!node) {
- return -EFAULT; // No VMA covers this address → segfault
- }
-
- VmAreaStruct* vma = reinterpret_cast<VmAreaStruct*>(
- reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb));
-
- if (!vma_allows(vma, cause)) {
- return -EFAULT;
- }
-
- /* Check if page is already mapped */
- uint64 paddr;
- if (pmap::lookup(space->root_pa, aligned_addr, &paddr)) {
- if (cause == VmFaultCause::Write && pmap::is_cow(space->root_pa, aligned_addr)) {
- /* Store Page Fault on a COW page */
- Page* page = pmm_alloc_page();
- if (!page) return -ENOMEM;
- uint64 new_pa = page_to_pa(page).raw;
-
- /* Copy page contents */
- uint8* dst = reinterpret_cast<uint8*>(pa_to_va(phys_addr(new_pa)).raw);
- const uint8* src = reinterpret_cast<const uint8*>(pa_to_va(phys_addr(paddr)).raw);
- for (uint64 i = 0; i < kPageSize; i++) {
- dst[i] = src[i];
- }
-
- /* Remap with write permission, clearing COW */
- uint64 pte_perm = pmap::vm_perm_to_pte(vma->perm);
- if (pmap::map(space->root_pa, aligned_addr, new_pa, pte_perm) != 0) {
- pmm_free_page(page);
- return -ENOMEM;
- }
-
- /* New page gets mapcount = 1 (we just mapped it) */
- page_inc_mapcount(page);
-
- /* Decrement mapcount and refcount of old page */
- Page* old_page = pa_to_page(phys_addr(paddr));
- page_dec_mapcount(old_page);
- page_decref(old_page);
-
- /* Flush TLB to remove old read-only cached translation */
- tlb_shootdown_page(space, aligned_addr);
- return 0;
- }
- /* DEBUG: page already mapped but still faulting — dump PTE */
- {
- uint64 cur_satp;
- asm volatile("csrr %0, satp" : "=r"(cur_satp));
- printk(KERN_ERR "fault: va=%llx pa=%llx cause=%d pte=%llx satp=%llx root=%llx\n",
- aligned_addr, paddr, static_cast<int>(cause),
- pmap::lookup_pte(space->root_pa, aligned_addr),
- cur_satp, space->root_pa);
- }
- return 0; // Already mapped, maybe read fault on existing page
- }
-
- /* Allocate physical page and zero it.
- * Linux: do_anonymous_page() uses alloc_zeroed_user_highpage_movable().
- * Anonymous pages MUST be zeroed — userspace expects MAP_ANONYMOUS
- * pages to contain all zeros (POSIX requirement). */
- Page* page = pmm_alloc_page();
- if (!page) return -ENOMEM;
-
- uint64 pa = page_to_pa(page).raw;
-
- /* Zero the page before mapping it to userspace */
- /* Zero the page before mapping it to userspace */
- uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
- for (uint64 i = 0; i < kPageSize; i++) kva[i] = 0;
-
- /* Translate MI permission flags to hardware PTE bits (MD layer) */
- uint64 pte_perm = pmap::vm_perm_to_pte(vma->perm);
-
- if (pmap::map(space->root_pa, aligned_addr, pa, pte_perm) != 0) {
- pmm_free_page(page);
- return -ENOMEM;
- }
-
- /* Initial mapping: refcount is already 1 from pmm_alloc_page.
- * Set mapcount = 1 to track this PTE mapping. */
- page_inc_mapcount(page);
-
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-/* Fork / Clone (COW infrastructure) */
-/* ------------------------------------------------------------------ */
-
-VmSpace* vmm_clone(VmSpace* parent) {
- if (!parent) return nullptr;
-
- VmSpace* child = VmSpace::create();
- if (!child) return nullptr;
-
- child->brk_start = parent->brk_start;
- child->brk_end = parent->brk_end;
-
- LockGuard parent_guard(parent->lock);
-
- struct rb_node* node = rb_first(&parent->vma_tree);
- while (node) {
- VmAreaStruct* parent_vma = reinterpret_cast<VmAreaStruct*>(
- reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb));
-
- VmAreaStruct* child_vma = new VmAreaStruct;
- if (!child_vma) {
- child->destroy();
- return nullptr;
- }
- child_vma->start = parent_vma->start;
- child_vma->end = parent_vma->end;
- child_vma->perm = parent_vma->perm;
- child_vma->mm = child;
-
- /* Copy pages and mark COW if writable */
- for (uint64 va = child_vma->start; va < child_vma->end; va += kPageSize) {
- uint64 pa;
- if (pmap::lookup(parent->root_pa, va, &pa)) {
- /* Make parent COW if it was writable */
- if (child_vma->perm & VM_WRITE) {
- pmap::make_cow(parent->root_pa, va);
- /* Flush TLB for parent since we reduced permissions */
- tlb_shootdown_page(parent, va);
- }
-
- /* Map in child as COW or read-only */
- uint64 pte_perm = pmap::vm_perm_to_pte(child_vma->perm);
- if (child_vma->perm & VM_WRITE) {
- pte_perm &= ~pmap::PTE_W;
- pte_perm |= pmap::PTE_COW;
- }
-
- if (pmap::map(child->root_pa, va, pa, pte_perm) != 0) {
- /* If map fails, we leak what we did in this loop for now,
- * but child->destroy() will clean it up safely since
- * we increment refcounts as we go. */
- delete child_vma;
- child->destroy();
- return nullptr;
- }
-
- /* Increment physical page refcount and mapcount */
- Page* shared_page = pa_to_page(phys_addr(pa));
- page_incref(shared_page);
- page_inc_mapcount(shared_page);
- }
- }
-
- rb_add(&child_vma->rb, &child->vma_tree, vma_less);
- node = rb_next(node);
- }
-
- return child;
-}
diff --git a/na/mm/vmm.cc b/na/mm/vmm.cc
deleted file mode 100644
index 51617e3..0000000
--- a/na/mm/vmm.cc
+++ /dev/null
@@ -1,235 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * vmm.cc — Virtual Memory Manager (Phase 1: Kernel Page Table)
- *
- * Builds a fine-grained kernel page table, activates it, and retires
- * the early_pgdir identity mapping from entry.S.
- *
- * With memblock integration, the initialization order is:
- * memblock_init → vmm_init (allocates page_map from memblock,
- * maps vmemmap, activates page table) → pmm_init (buddy system
- * starts with page_map already at vmemmap VA — no relocation).
- *
- * Reference: docs/specs/vmm.md
- */
-
-#include <arch/config.h>
-#include <arch/mmu.h>
-#include <arch/pmap.h>
-#include <lume/tlb.h>
-#include <lume/addr.h>
-#include <lume/console.h>
-#include <lume/fdt.h>
-#include <lume/page.h>
-#include <lume/klog.h>
-#include <lume/kprintf.h>
-#include <lume/memblock.h>
-#include <lume/pmm.h>
-#include <lume/types.h>
-#include <lume/vmm.h>
-#include <lume/config.h>
-
-/* Linker-exported section boundaries (virtual addresses) */
-extern "C" char _stext[], _etext[];
-extern "C" char _srodata[], _erodata[];
-extern "C" char _sdata[], _edata[];
-extern "C" char _sbss[], _ebss[];
-extern "C" char _kernel_end[];
-
-/* Global kernel page table root PA — external linkage for task_exit */
-uint64 g_kernel_pgtbl = 0;
-
-namespace {
-
-/* VMM exports for PMM: page_map location after vmemmap mapping. */
-uint64 g_vmemmap_page_map_pa = 0;
-uint64 g_vmemmap_page_map_size = 0;
-uint64 g_vmemmap_num_pages = 0;
-
-/* Convert a linker symbol to PA (kernel is executing in higher-half VA). */
-inline uint64 sym_to_pa(const void *sym) {
- return boot_va_to_pa(virt_addr(reinterpret_cast<uint64>(sym))).raw;
-}
-
-/* Map a PA range into the kernel page table via the direct map. */
-void map_range(uint64 root, uint64 pa_start, uint64 pa_end,
- uint64 perm) {
- for (uint64 pa = pa_start; pa < pa_end; pa += kPageSize) {
- uint64 va = pa_to_va(phys_addr(pa)).raw;
- int ret = pmap::map(root, va, pa, perm | pmap::PTE_G);
- if (ret != 0) {
- kernel_panic("vmm_init: pmap::map failed");
- }
- }
-}
-
-/* Map a PA range into the kernel page table at the fixed linked VA. */
-void map_range_linked(uint64 root, uint64 pa_start, uint64 pa_end,
- uint64 perm) {
- for (uint64 pa = pa_start; pa < pa_end; pa += kPageSize) {
- uint64 va = boot_pa_to_va(phys_addr(pa)).raw;
- int ret = pmap::map(root, va, pa, perm | pmap::PTE_G);
- if (ret != 0) {
- kernel_panic("vmm_init: pmap::map (linked) failed");
- }
- }
-}
-
-/* Map a PA range to an explicit VA base, using 2MB pages where possible. */
-void map_range_at_huge(uint64 root, uint64 va_base, uint64 pa_start,
- uint64 pa_end, uint64 perm) {
- constexpr uint64 k2MB = 2ULL * 1024 * 1024;
- uint64 pa = pa_start;
- uint64 va = va_base;
-
- /* Map 2MB-aligned chunks with mega pages */
- while (pa + k2MB <= pa_end) {
- if ((va & (k2MB - 1)) == 0 && (pa & (k2MB - 1)) == 0) {
- int ret = pmap::map_2mb(root, va, pa, perm | pmap::PTE_G);
- if (ret != 0) {
- kernel_panic("vmm_init: pmap::map_2mb failed");
- }
- pa += k2MB;
- va += k2MB;
- } else {
- break;
- }
- }
-
- /* Map remaining 4KB tail */
- while (pa < pa_end) {
- int ret = pmap::map(root, va, pa, perm | pmap::PTE_G);
- if (ret != 0) {
- kernel_panic("vmm_init: vmemmap 4KB pmap::map failed");
- }
- pa += kPageSize;
- va += kPageSize;
- }
-}
-
-} // namespace
-
-uint64 vmm_get_page_map_pa() { return g_vmemmap_page_map_pa; }
-uint64 vmm_get_page_map_size() { return g_vmemmap_page_map_size; }
-uint64 vmm_get_num_pages() { return g_vmemmap_num_pages; }
-
-void vmm_init() {
- /* 1. Allocate new root page table (from memblock) */
- uint64 root = pmap::create();
- if (!root) {
- kernel_panic("vmm_init: failed to create root page table");
- }
-
- /* 2. Compute PA boundaries of each kernel section */
- uint64 text_start = page_align_down(sym_to_pa(_stext));
- uint64 text_end = page_align_up(sym_to_pa(_etext));
- uint64 ro_start = page_align_down(sym_to_pa(_srodata));
- uint64 ro_end = page_align_up(sym_to_pa(_erodata));
- uint64 data_start = page_align_down(sym_to_pa(_sdata));
- uint64 bss_end = page_align_up(sym_to_pa(_ebss));
-
- /* Memory extent from memblock */
- uint64 mem_base = memblock_get_mem_base();
- uint64 mem_size = memblock_get_mem_size();
- uint64 mem_end = mem_base + mem_size;
-
- /* 3. Map kernel image at FIXED linked VA.
- *
- * Kernel text/data are linked at the bootstrap higher-half addresses
- * (kDirectMapBaseDefault + PA). They must remain executable/readable at
- * those addresses until full kernel-image relocation is implemented.
- */
- map_range_linked(root, text_start, text_end, pmap::PTE_R | pmap::PTE_X);
-
- /* 4. Map .rodata (R) at fixed linked VA.
- * We map [_srodata, _sdata) so that the .lume.reloc section (which
- * sits in its own padded output section between _erodata and _sdata)
- * is also covered. Relocator only reads it during early boot, but
- * mapping it keeps post-vmm accesses to __lume_reloc_* symbols sane. */
- map_range_linked(root, ro_start, page_align_down(sym_to_pa(_sdata)),
- pmap::PTE_R);
-
- /* 5. Map .data + .bss (RW) at fixed linked VA */
- map_range_linked(root, data_start, bss_end, pmap::PTE_R | pmap::PTE_W);
-
- /* 6. Map usable physical RAM into the KASLR-slid direct map.
- *
- * The direct map provides pa_to_va() / va_to_pa() for the physical
- * address space. We map from kPhysStart (the kernel's linked load
- * address), skipping [mem_base, kPhysStart) because that region is
- * PMP-protected firmware (OpenSBI) — S-mode cannot access it.
- *
- * Linux does the same on RISC-V: the linear map covers only the
- * usable RAM range, not firmware-reserved regions.
- *
- * Uses 2MB huge pages for efficiency (64 entries for 128MB vs 32K).
- */
- uint64 dm_start = kPhysStart;
- if (dm_start < mem_base) {
- dm_start = mem_base;
- }
- uint64 dm_va_base = pa_to_va(phys_addr(dm_start)).raw;
- map_range_at_huge(root, dm_va_base, dm_start, mem_end,
- pmap::PTE_R | pmap::PTE_W);
-
- /* 7. Allocate page_map from memblock (top of RAM) and map at VMEMMAP.
- *
- * Since memblock is active (PMM not yet initialized), pmap::map
- * allocates page table pages from memblock too. page_map starts
- * life at the vmemmap VA — no relocation ever needed.
- */
- uint64 num_pages = mem_size / kPageSize;
- uint64 page_map_bytes = num_pages * sizeof(Page);
- uint64 page_map_span = page_align_up(page_map_bytes);
- uint64 page_map_pa = memblock_alloc_top(page_map_span, kPageSize);
-
- if (!page_map_pa) {
- kernel_panic("vmm_init: memblock failed to allocate page_map");
- }
-
- kprintf("[vmm] vmemmap: %llu pages, %llu KB at PA 0x%llx\n", num_pages,
- page_map_span / 1024, page_map_pa);
-
- /* Map page_map at vmemmap_base from KaslrLayout (randomised by KASLR), using 2MB pages where possible */
- map_range_at_huge(root, arch::kaslr_layout_get().vmemmap_base, page_map_pa,
- page_map_pa + page_map_span, pmap::PTE_R | pmap::PTE_W);
-
- /* Export for pmm_init() */
- g_vmemmap_page_map_pa = page_map_pa;
- g_vmemmap_page_map_size = page_map_bytes;
- g_vmemmap_num_pages = num_pages;
-
- /* 8. Pre-map console MMIO before page table switch */
- g_kernel_pgtbl = root;
- console_init();
-
- /* 9. Activate the new page table */
- pmap::activate(root);
-
- /* Console MMIO access must now use runtime (KASLR) direct-map base. */
- console_use_runtime_mapping();
-
- kprintf("[vmm] vmm_init complete (vmemmap at 0x%llx)\n", arch::kaslr_layout_get().vmemmap_base);
-}
-
-void vmm_init_ap() {
- if (!g_kernel_pgtbl) {
- kernel_panic("vmm_init_ap: kernel page table not ready");
- }
- pmap::activate(g_kernel_pgtbl);
-}
-
-void vmm_map_kernel_mmio(uint64 pa, uint64 size) {
- if (!g_kernel_pgtbl) {
- kernel_panic("vmm_map_kernel_mmio: kernel page table not ready");
- }
-
- uint64 aligned_pa = page_align_down(pa);
- uint64 aligned_size = page_align_up(pa + size) - aligned_pa;
-
- map_range(g_kernel_pgtbl, aligned_pa, aligned_pa + aligned_size,
- pmap::PTE_R | pmap::PTE_W | pmap::PTE_G);
- tlb_shootdown_all();
-}
diff --git a/na/scripts/Kbuild.include b/na/scripts/Kbuild.include
deleted file mode 100644
index 76466bd..0000000
--- a/na/scripts/Kbuild.include
+++ /dev/null
@@ -1,110 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (C) 2026 Richard Qin
-#
-# scripts/Kbuild.include — shared Make helpers for LumeOS.
-#
-# Mirrors a small, useful subset of Linux's scripts/Kbuild.include.
-# Provides:
-#
-# - Q / KBUILD_VERBOSE : `make V=1` shows full commands,
-# default V=0 prints a one-line summary
-# - $(call cmd,<name>) : echo a quiet summary, then run cmd_<name>
-# - $(call if_changed,<name>) : currently a thin alias for $(cmd) —
-# no .cmd-file hashing yet (Linux's full
-# if_changed uses fixdep to detect both
-# recipe-text and header changes; LumeOS
-# relies on `-MMD -MP` for headers and
-# on `make clean` for flag changes).
-# - $(call kbuild-objs,...) : translate Kbuild-style `obj-y := foo.o`
-# entries into SRCS_C / SRCS_CC.
-#
-# Naming convention adopted from Linux: each command has both a
-# `quiet_cmd_<name>` (single-line summary) and a `cmd_<name>` (full
-# command line). $(quiet) selects which prefix to display:
-# V=0 -> quiet := quiet_ -> echoes " CC foo.c"
-# V=1 -> quiet := -> echoes the full $(CC) $(CFLAGS) ...
-# ----------------------------------------------------------------------
-
-# --- Verbosity ---------------------------------------------------------
-# Override at the command line: `make V=1`. Default V=0 = quiet.
-KBUILD_VERBOSE ?= $(V)
-KBUILD_VERBOSE ?= 0
-
-ifeq ($(KBUILD_VERBOSE),1)
- Q :=
- quiet :=
-else
- Q := @
- quiet := quiet_
-endif
-
-# --- echo-cmd / cmd / if_changed ---------------------------------------
-#
-# echo-cmd prints the recipe's one-line summary (when quiet) or its full
-# command line (when verbose). Implementation uses $($(quiet)cmd_<name>)
-# so the right form is picked up automatically.
-#
-# cmd is the workhorse: print summary, then execute the real command.
-echo-cmd = $(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))';)
-
-cmd = @$(echo-cmd) $(cmd_$(1))
-
-# if_changed currently degenerates to `cmd` — full Linux semantics
-# require tracking the command-line via per-target .<file>.cmd files
-# and comparing on every rebuild, which needs fixdep. Until LumeOS
-# grows that infrastructure, `make clean` after editing CFLAGS suffices.
-if_changed = $(cmd)
-
-# --- Standard quiet_cmd_* / cmd_* templates ---------------------------
-#
-# Subdir Makefiles and the top-level Makefile can reference these by
-# name via $(call cmd,<name>). Add new pairs here as new recipe shapes
-# show up (e.g., cmd_lz / cmd_gz / cmd_zstd for compressed Image targets).
-
- quiet_cmd_cc_o_c = CC $<
- cmd_cc_o_c = $(CC) $(CFLAGS_LIBC) -c $< -o $@
-
- quiet_cmd_cxx_o_cc = CXX $<
- cmd_cxx_o_cc = $(CXX) $(CXXFLAGS) -c $< -o $@
-
- quiet_cmd_as_o_S = AS $<
- cmd_as_o_S = $(CC) $(ASFLAGS) -c $< -o $@
-
- quiet_cmd_cc_s_c = CC -S $<
- cmd_cc_s_c = $(CC) $(CFLAGS_LIBC) -S $< -o $@
-
- quiet_cmd_objcopy_bin = OBJCOPY $@
- cmd_objcopy_bin = $(OBJCOPY) -O binary $< $@
-
-# Objcopy with custom flags via per-target OBJCOPYFLAGS variable —
-# used by the .pi.o post-processing rule (--prefix-symbols=__pi_ etc.).
- quiet_cmd_objcopy = OBJCOPY $@
- cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $< $@
-
- quiet_cmd_ld_kernel = LD $@
- cmd_ld_kernel = $(LD) $(LDFLAGS) -T $(LINKER_SCRIPT) -o $@ $^
-
-# --- kbuild-objs: obj-y -> SRCS_C / SRCS_CC ---------------------------
-#
-# Usage from a subdir Makefile (e.g. arch/riscv/kernel/pi/Makefile):
-#
-# obj-dir := arch/riscv/kernel/pi
-# obj-y := fdt_early.pi.o cmdline_early.pi.o archrandom_early.pi.o
-# $(eval $(call kbuild-objs,$(obj-dir),$(obj-y:.pi.o=.o)))
-#
-# For each <name>.o entry, looks up <dir>/<name>.cc first, then
-# <dir>/<name>.c, and appends to SRCS_CC / SRCS_C respectively.
-# Pure .pi.o entries should be passed through `:.pi.o=.o` first by the
-# caller so the .pi suffix doesn't confuse the source-file lookup.
-
-define _kbuild-find-src
-$(if $(wildcard $(1)/$(basename $(2)).cc),\
- $(eval SRCS_CC += $(1)/$(basename $(2)).cc),\
- $(if $(wildcard $(1)/$(basename $(2)).c),\
- $(eval SRCS_C += $(1)/$(basename $(2)).c),\
- $(error kbuild-objs: no $(1)/$(basename $(2)).{c,cc} for obj $(2))))
-endef
-
-define kbuild-objs
-$(foreach _obj,$(2),$(call _kbuild-find-src,$(1),$(_obj)))
-endef
diff --git a/na/scripts/Kconfig.mk b/na/scripts/Kconfig.mk
deleted file mode 100644
index 2bdc934..0000000
--- a/na/scripts/Kconfig.mk
+++ /dev/null
@@ -1,238 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (C) 2026 Richard Qin
-#
-# scripts/Kconfig.mk — Kernel Configuration Infrastructure
-#
-# This file is the heart of the LumeOS build-time configuration system.
-# It mirrors the role of Linux's Kconfig + auto.conf pipeline:
-#
-# 1. Developer edits `.config` (or copies from arch/$(ARCH)/defconfig)
-# 2. This file reads `.config`, applies defaults for anything missing,
-# validates constraints, and exports two things:
-# a) KCPPFLAGS — a set of -DCONFIG_xxx=value flags for the compiler
-# b) $(BUILD)/generated/autoconf.h — the same values as C #defines
-#
-# Design Principles (matching Linux conventions):
-# - Every CONFIG_ symbol has a default defined HERE, so a bare `make`
-# with no .config still builds a sane kernel.
-# - Boolean options use 'y' / 'n'. Numeric options are plain integers.
-# - The generated autoconf.h uses the exact same names so that
-# `#ifdef CONFIG_DEBUG` works identically in Makefile and C.
-# ======================================================================
-
-# ------------------------------------------------------------------
-# §1 Read user configuration (if it exists)
-# ------------------------------------------------------------------
--include .config
-
-# ------------------------------------------------------------------
-# §2 Provide defaults for every CONFIG_ symbol
-#
-# ?= means "set only if not already set (by .config or CLI)".
-# This is the single source of truth for default values.
-# ------------------------------------------------------------------
-
-# --- Platform -------------------------------------------------------
-CONFIG_ARCH ?= riscv
-
-# --- CPU / SMP ------------------------------------------------------
-CONFIG_NR_CPUS ?= 8
-
-# --- Scheduler ------------------------------------------------------
-CONFIG_HZ ?= 100
-CONFIG_NR_PRIORITIES ?= 32
-
-# --- Memory Management ----------------------------------------------
-CONFIG_PAGE_SIZE ?= 4096
-CONFIG_MAX_ORDER ?= 11
-CONFIG_KERNEL_STACK_SIZE ?= 8192
-
-# --- Security / Hardening -------------------------------------------
-CONFIG_KASLR ?= y
-CONFIG_KASLR_POLICY_ID ?= 0
-CONFIG_KASLR_LOG_LEVEL ?= 1
-CONFIG_STACK_CANARY ?= y
-CONFIG_KERNEL_COMPRESSED ?= y
-
-# --- Debug -----------------------------------------------------------
-CONFIG_DEBUG ?= y
-CONFIG_DEBUG_LOCKS ?= y
-CONFIG_SELFTEST ?= y
-CONFIG_KLOG ?= y
-
-# --- Initramfs ------------------------------------------------------
-# Source for the initramfs image. Accepted forms (auto-detected by
-# scripts/gen_initramfs.sh):
-# - a listing file (gen_init_cpio format) → built into a cpio
-# - a directory → packed recursively
-# - a *.cpio file → used as-is
-# Override on the command line or pin in .config:
-# make all CONFIG_INITRAMFS_SOURCE=/path/to/rootfs
-CONFIG_INITRAMFS_SOURCE ?= usr/default_cpio_list
-CONFIG_INITRAMFS_ROOT_UID ?= 0
-CONFIG_INITRAMFS_ROOT_GID ?= 0
-
-# ------------------------------------------------------------------
-# §3 Derived constants (computed from CONFIG_ symbols)
-#
-# These follow Linux's pattern of expressing dependent values
-# so that C code never has to do its own derivation at runtime.
-# ------------------------------------------------------------------
-
-# Default priority = middle of the range
-CONFIG_DEFAULT_PRIORITY = $(shell echo $$(( $(CONFIG_NR_PRIORITIES) / 2 )))
-
-# Idle priority = lowest = NR_PRIORITIES - 1
-CONFIG_IDLE_PRIORITY = $(shell echo $$(( $(CONFIG_NR_PRIORITIES) - 1 )))
-
-# Default time slice in ticks: 100ms worth of ticks
-# (same as Linux CFS's sysctl_sched_min_granularity default)
-CONFIG_DEFAULT_TIMESLICE = $(shell echo $$(( $(CONFIG_HZ) / 10 )))
-
-# ------------------------------------------------------------------
-# §4 Export to C/C++ via -D flags
-#
-# KCPPFLAGS is appended to CFLAGS/CXXFLAGS in the main Makefile.
-# Every CONFIG_xxx=<value> becomes -DCONFIG_xxx=<value>.
-# Boolean 'y' options become -DCONFIG_xxx=1.
-# ------------------------------------------------------------------
-KCPPFLAGS :=
-
-# Numeric options — pass through as-is
-KCPPFLAGS += -DCONFIG_NR_CPUS=$(CONFIG_NR_CPUS)
-KCPPFLAGS += -DCONFIG_HZ=$(CONFIG_HZ)
-KCPPFLAGS += -DCONFIG_NR_PRIORITIES=$(CONFIG_NR_PRIORITIES)
-KCPPFLAGS += -DCONFIG_DEFAULT_PRIORITY=$(CONFIG_DEFAULT_PRIORITY)
-KCPPFLAGS += -DCONFIG_IDLE_PRIORITY=$(CONFIG_IDLE_PRIORITY)
-KCPPFLAGS += -DCONFIG_DEFAULT_TIMESLICE=$(CONFIG_DEFAULT_TIMESLICE)
-KCPPFLAGS += -DCONFIG_PAGE_SIZE=$(CONFIG_PAGE_SIZE)
-KCPPFLAGS += -DCONFIG_MAX_ORDER=$(CONFIG_MAX_ORDER)
-KCPPFLAGS += -DCONFIG_KERNEL_STACK_SIZE=$(CONFIG_KERNEL_STACK_SIZE)
-
-# Boolean options — translate 'y' → 1
-ifeq ($(CONFIG_KASLR),y)
-KCPPFLAGS += -DCONFIG_KASLR=1
-endif
-KCPPFLAGS += -DCONFIG_KASLR_POLICY_ID=$(CONFIG_KASLR_POLICY_ID)
-KCPPFLAGS += -DCONFIG_KASLR_LOG_LEVEL=$(CONFIG_KASLR_LOG_LEVEL)
-ifeq ($(CONFIG_STACK_CANARY),y)
-KCPPFLAGS += -DCONFIG_STACK_CANARY=1
-endif
-ifeq ($(CONFIG_DEBUG),y)
-KCPPFLAGS += -DCONFIG_DEBUG=1
-endif
-ifeq ($(CONFIG_DEBUG_LOCKS),y)
-KCPPFLAGS += -DCONFIG_DEBUG_LOCKS=1
-endif
-ifeq ($(CONFIG_SELFTEST),y)
-KCPPFLAGS += -DCONFIG_SELFTEST=1
-endif
-ifeq ($(CONFIG_KLOG),y)
-KCPPFLAGS += -DCONFIG_KLOG=1
-endif
-ifeq ($(CONFIG_KERNEL_COMPRESSED),y)
-KCPPFLAGS += -DCONFIG_KERNEL_COMPRESSED=1
-endif
-
-# ------------------------------------------------------------------
-# §5 Generate autoconf.h (analogous to Linux's include/generated/autoconf.h)
-#
-# This header is auto-generated at build time so that C code can
-# #include <generated/autoconf.h> and see CONFIG_xxx macros without
-# relying on -D flags alone. This is important for IDE integration.
-# ------------------------------------------------------------------
-AUTOCONF_H := $(BUILD)/generated/autoconf.h
-
-define generate-autoconf
- @mkdir -p $(dir $(AUTOCONF_H))
- @echo " GEN $(AUTOCONF_H)"
- @echo "/* AUTO-GENERATED — DO NOT EDIT */" > $(AUTOCONF_H)
- @echo "/* Generated by scripts/Kconfig.mk */" >> $(AUTOCONF_H)
- @echo "#pragma once" >> $(AUTOCONF_H)
- @echo "" >> $(AUTOCONF_H)
- @echo "/* Platform */" >> $(AUTOCONF_H)
- @echo "#define CONFIG_NR_CPUS $(CONFIG_NR_CPUS)" >> $(AUTOCONF_H)
- @echo "" >> $(AUTOCONF_H)
- @echo "/* Scheduler */" >> $(AUTOCONF_H)
- @echo "#define CONFIG_HZ $(CONFIG_HZ)" >> $(AUTOCONF_H)
- @echo "#define CONFIG_NR_PRIORITIES $(CONFIG_NR_PRIORITIES)" >> $(AUTOCONF_H)
- @echo "#define CONFIG_DEFAULT_PRIORITY $(CONFIG_DEFAULT_PRIORITY)" >> $(AUTOCONF_H)
- @echo "#define CONFIG_IDLE_PRIORITY $(CONFIG_IDLE_PRIORITY)" >> $(AUTOCONF_H)
- @echo "#define CONFIG_DEFAULT_TIMESLICE $(CONFIG_DEFAULT_TIMESLICE)" >> $(AUTOCONF_H)
- @echo "" >> $(AUTOCONF_H)
- @echo "/* Memory Management */" >> $(AUTOCONF_H)
- @echo "#define CONFIG_PAGE_SIZE $(CONFIG_PAGE_SIZE)" >> $(AUTOCONF_H)
- @echo "#define CONFIG_MAX_ORDER $(CONFIG_MAX_ORDER)" >> $(AUTOCONF_H)
- @echo "#define CONFIG_KERNEL_STACK_SIZE $(CONFIG_KERNEL_STACK_SIZE)" >> $(AUTOCONF_H)
- @echo "" >> $(AUTOCONF_H)
- @echo "/* Security */" >> $(AUTOCONF_H)
- $(if $(filter y,$(CONFIG_KASLR)), @echo "#define CONFIG_KASLR 1" >> $(AUTOCONF_H))
- @echo "#define CONFIG_KASLR_POLICY_ID $(CONFIG_KASLR_POLICY_ID)" >> $(AUTOCONF_H)
- @echo "#define CONFIG_KASLR_LOG_LEVEL $(CONFIG_KASLR_LOG_LEVEL)" >> $(AUTOCONF_H)
- $(if $(filter y,$(CONFIG_STACK_CANARY)), @echo "#define CONFIG_STACK_CANARY 1" >> $(AUTOCONF_H))
- @echo "" >> $(AUTOCONF_H)
- @echo "/* Debug */" >> $(AUTOCONF_H)
- $(if $(filter y,$(CONFIG_DEBUG)), @echo "#define CONFIG_DEBUG 1" >> $(AUTOCONF_H))
- $(if $(filter y,$(CONFIG_DEBUG_LOCKS)), @echo "#define CONFIG_DEBUG_LOCKS 1" >> $(AUTOCONF_H))
- $(if $(filter y,$(CONFIG_SELFTEST)), @echo "#define CONFIG_SELFTEST 1" >> $(AUTOCONF_H))
- $(if $(filter y,$(CONFIG_KLOG)), @echo "#define CONFIG_KLOG 1" >> $(AUTOCONF_H))
- $(if $(filter y,$(CONFIG_KERNEL_COMPRESSED)), @echo "#define CONFIG_KERNEL_COMPRESSED 1" >> $(AUTOCONF_H))
-endef
-
-# ------------------------------------------------------------------
-# §6 Utility targets
-# ------------------------------------------------------------------
-
-# 'make defconfig' — reset .config to the arch default
-.PHONY: defconfig savedefconfig showconfig
-
-defconfig:
- @echo " DEFCFG arch/$(CONFIG_ARCH)/defconfig → .config"
- @cp arch/$(CONFIG_ARCH)/defconfig .config
-
-# 'make savedefconfig' — save current effective config
-savedefconfig:
- @echo " SAVE .config"
- @echo "# LumeOS Kernel Configuration (saved $$(date +%Y-%m-%d))" > .config
- @echo "CONFIG_ARCH = $(CONFIG_ARCH)" >> .config
- @echo "CONFIG_NR_CPUS = $(CONFIG_NR_CPUS)" >> .config
- @echo "CONFIG_HZ = $(CONFIG_HZ)" >> .config
- @echo "CONFIG_NR_PRIORITIES = $(CONFIG_NR_PRIORITIES)" >> .config
- @echo "CONFIG_PAGE_SIZE = $(CONFIG_PAGE_SIZE)" >> .config
- @echo "CONFIG_MAX_ORDER = $(CONFIG_MAX_ORDER)" >> .config
- @echo "CONFIG_KERNEL_STACK_SIZE = $(CONFIG_KERNEL_STACK_SIZE)" >> .config
- @echo "CONFIG_KASLR = $(CONFIG_KASLR)" >> .config
- @echo "CONFIG_KASLR_POLICY_ID = $(CONFIG_KASLR_POLICY_ID)" >> .config
- @echo "CONFIG_KASLR_LOG_LEVEL = $(CONFIG_KASLR_LOG_LEVEL)" >> .config
- @echo "CONFIG_STACK_CANARY = $(CONFIG_STACK_CANARY)" >> .config
- @echo "CONFIG_DEBUG = $(CONFIG_DEBUG)" >> .config
- @echo "CONFIG_DEBUG_LOCKS = $(CONFIG_DEBUG_LOCKS)" >> .config
- @echo "CONFIG_SELFTEST = $(CONFIG_SELFTEST)" >> .config
- @echo "CONFIG_KLOG = $(CONFIG_KLOG)" >> .config
- @echo "CONFIG_KERNEL_COMPRESSED = $(CONFIG_KERNEL_COMPRESSED)" >> .config
-
-# 'make showconfig' — print effective configuration
-showconfig:
- @echo "========================================"
- @echo " LumeOS Effective Configuration"
- @echo "========================================"
- @echo " ARCH = $(CONFIG_ARCH)"
- @echo " NR_CPUS = $(CONFIG_NR_CPUS)"
- @echo " HZ = $(CONFIG_HZ)"
- @echo " NR_PRIORITIES = $(CONFIG_NR_PRIORITIES)"
- @echo " DEFAULT_PRIORITY = $(CONFIG_DEFAULT_PRIORITY)"
- @echo " IDLE_PRIORITY = $(CONFIG_IDLE_PRIORITY)"
- @echo " DEFAULT_TIMESLICE = $(CONFIG_DEFAULT_TIMESLICE)"
- @echo " PAGE_SIZE = $(CONFIG_PAGE_SIZE)"
- @echo " MAX_ORDER = $(CONFIG_MAX_ORDER)"
- @echo " KERNEL_STACK_SIZE = $(CONFIG_KERNEL_STACK_SIZE)"
- @echo " KASLR = $(CONFIG_KASLR)"
- @echo " KASLR_POLICY_ID = $(CONFIG_KASLR_POLICY_ID)"
- @echo " KASLR_LOG_LEVEL = $(CONFIG_KASLR_LOG_LEVEL)"
- @echo " STACK_CANARY = $(CONFIG_STACK_CANARY)"
- @echo " DEBUG = $(CONFIG_DEBUG)"
- @echo " DEBUG_LOCKS = $(CONFIG_DEBUG_LOCKS)"
- @echo " SELFTEST = $(CONFIG_SELFTEST)"
- @echo " KLOG = $(CONFIG_KLOG)"
- @echo " KERNEL_COMPRESSED = $(CONFIG_KERNEL_COMPRESSED)"
- @echo "========================================"
diff --git a/na/scripts/dtc/libfdt/fdt.c b/na/scripts/dtc/libfdt/fdt.c
deleted file mode 100644
index 56d4dcb..0000000
--- a/na/scripts/dtc/libfdt/fdt.c
+++ /dev/null
@@ -1,347 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-/*
- * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
- * that the given buffer contains what appears to be a flattened
- * device tree with sane information in its header.
- */
-int32_t fdt_ro_probe_(const void *fdt)
-{
- uint32_t totalsize = fdt_totalsize(fdt);
-
- if (can_assume(VALID_DTB))
- return totalsize;
-
- /* The device tree must be at an 8-byte aligned address */
- if ((uintptr_t)fdt & 7)
- return -FDT_ERR_ALIGNMENT;
-
- if (fdt_magic(fdt) == FDT_MAGIC) {
- /* Complete tree */
- if (!can_assume(LATEST)) {
- if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
- return -FDT_ERR_BADVERSION;
- if (fdt_last_comp_version(fdt) >
- FDT_LAST_SUPPORTED_VERSION)
- return -FDT_ERR_BADVERSION;
- }
- } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
- /* Unfinished sequential-write blob */
- if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
- return -FDT_ERR_BADSTATE;
- } else {
- return -FDT_ERR_BADMAGIC;
- }
-
- if (totalsize < INT32_MAX)
- return totalsize;
- else
- return -FDT_ERR_TRUNCATED;
-}
-
-static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
-{
- return (off >= hdrsize) && (off <= totalsize);
-}
-
-static int check_block_(uint32_t hdrsize, uint32_t totalsize,
- uint32_t base, uint32_t size)
-{
- if (!check_off_(hdrsize, totalsize, base))
- return 0; /* block start out of bounds */
- if ((base + size) < base)
- return 0; /* overflow */
- if (!check_off_(hdrsize, totalsize, base + size))
- return 0; /* block end out of bounds */
- return 1;
-}
-
-size_t fdt_header_size_(uint32_t version)
-{
- if (version <= 1)
- return FDT_V1_SIZE;
- else if (version <= 2)
- return FDT_V2_SIZE;
- else if (version <= 3)
- return FDT_V3_SIZE;
- else if (version <= 16)
- return FDT_V16_SIZE;
- else
- return FDT_V17_SIZE;
-}
-
-size_t fdt_header_size(const void *fdt)
-{
- return can_assume(LATEST) ? FDT_V17_SIZE :
- fdt_header_size_(fdt_version(fdt));
-}
-
-int fdt_check_header(const void *fdt)
-{
- size_t hdrsize;
-
- /* The device tree must be at an 8-byte aligned address */
- if ((uintptr_t)fdt & 7)
- return -FDT_ERR_ALIGNMENT;
-
- if (fdt_magic(fdt) != FDT_MAGIC)
- return -FDT_ERR_BADMAGIC;
- if (!can_assume(LATEST)) {
- if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
- || (fdt_last_comp_version(fdt) >
- FDT_LAST_SUPPORTED_VERSION))
- return -FDT_ERR_BADVERSION;
- if (fdt_version(fdt) < fdt_last_comp_version(fdt))
- return -FDT_ERR_BADVERSION;
- }
- hdrsize = fdt_header_size(fdt);
- if (!can_assume(VALID_DTB)) {
- if ((fdt_totalsize(fdt) < hdrsize)
- || (fdt_totalsize(fdt) > INT_MAX))
- return -FDT_ERR_TRUNCATED;
-
- /* memrsv block must be 8 byte aligned */
- if (fdt_off_mem_rsvmap(fdt) % sizeof(uint64_t))
- return -FDT_ERR_ALIGNMENT;
-
- /* Structure block must be 4 byte aligned */
- if (fdt_off_dt_struct(fdt) % FDT_TAGSIZE)
- return -FDT_ERR_ALIGNMENT;
-
- /* Bounds check memrsv block */
- if (!check_off_(hdrsize, fdt_totalsize(fdt),
- fdt_off_mem_rsvmap(fdt)))
- return -FDT_ERR_TRUNCATED;
-
- /* Bounds check structure block */
- if (!can_assume(LATEST) && fdt_version(fdt) < 17) {
- if (!check_off_(hdrsize, fdt_totalsize(fdt),
- fdt_off_dt_struct(fdt)))
- return -FDT_ERR_TRUNCATED;
- } else {
- if (!check_block_(hdrsize, fdt_totalsize(fdt),
- fdt_off_dt_struct(fdt),
- fdt_size_dt_struct(fdt)))
- return -FDT_ERR_TRUNCATED;
- }
-
- /* Bounds check strings block */
- if (!check_block_(hdrsize, fdt_totalsize(fdt),
- fdt_off_dt_strings(fdt),
- fdt_size_dt_strings(fdt)))
- return -FDT_ERR_TRUNCATED;
- }
-
- return 0;
-}
-
-const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
-{
- unsigned int uoffset = offset;
- unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
-
- if (offset < 0)
- return NULL;
-
- if (!can_assume(VALID_INPUT))
- if ((absoffset < uoffset)
- || ((absoffset + len) < absoffset)
- || (absoffset + len) > fdt_totalsize(fdt))
- return NULL;
-
- if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
- if (((uoffset + len) < uoffset)
- || ((offset + len) > fdt_size_dt_struct(fdt)))
- return NULL;
-
- return fdt_offset_ptr_(fdt, offset);
-}
-
-uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
-{
- const fdt32_t *tagp, *lenp;
- uint32_t tag, len, sum;
- int offset = startoffset;
- const char *p;
-
- *nextoffset = -FDT_ERR_TRUNCATED;
- tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
- if (!can_assume(VALID_DTB) && !tagp)
- return FDT_END; /* premature end */
- tag = fdt32_to_cpu(*tagp);
- offset += FDT_TAGSIZE;
-
- *nextoffset = -FDT_ERR_BADSTRUCTURE;
- switch (tag) {
- case FDT_BEGIN_NODE:
- /* skip name */
- do {
- p = fdt_offset_ptr(fdt, offset++, 1);
- } while (p && (*p != '\0'));
- if (!can_assume(VALID_DTB) && !p)
- return FDT_END; /* premature end */
- break;
-
- case FDT_PROP:
- lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
- if (!can_assume(VALID_DTB) && !lenp)
- return FDT_END; /* premature end */
-
- len = fdt32_to_cpu(*lenp);
- sum = len + offset;
- if (!can_assume(VALID_DTB) &&
- (INT_MAX <= sum || sum < (uint32_t) offset))
- return FDT_END; /* premature end */
-
- /* skip-name offset, length and value */
- offset += sizeof(struct fdt_property) - FDT_TAGSIZE + len;
-
- if (!can_assume(LATEST) &&
- fdt_version(fdt) < 0x10 && len >= 8 &&
- ((offset - len) % 8) != 0)
- offset += 4;
- break;
-
- case FDT_END:
- case FDT_END_NODE:
- case FDT_NOP:
- break;
-
- default:
- return FDT_END;
- }
-
- if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
- return FDT_END; /* premature end */
-
- *nextoffset = FDT_TAGALIGN(offset);
- return tag;
-}
-
-int fdt_check_node_offset_(const void *fdt, int offset)
-{
- if (!can_assume(VALID_INPUT)
- && ((offset < 0) || (offset % FDT_TAGSIZE)))
- return -FDT_ERR_BADOFFSET;
-
- if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
- return -FDT_ERR_BADOFFSET;
-
- return offset;
-}
-
-int fdt_check_prop_offset_(const void *fdt, int offset)
-{
- if (!can_assume(VALID_INPUT)
- && ((offset < 0) || (offset % FDT_TAGSIZE)))
- return -FDT_ERR_BADOFFSET;
-
- if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
- return -FDT_ERR_BADOFFSET;
-
- return offset;
-}
-
-int fdt_next_node(const void *fdt, int offset, int *depth)
-{
- int nextoffset = 0;
- uint32_t tag;
-
- if (offset >= 0)
- if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
- return nextoffset;
-
- do {
- offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
-
- switch (tag) {
- case FDT_PROP:
- case FDT_NOP:
- break;
-
- case FDT_BEGIN_NODE:
- if (depth)
- (*depth)++;
- break;
-
- case FDT_END_NODE:
- if (depth && ((--(*depth)) < 0))
- return nextoffset;
- break;
-
- case FDT_END:
- if ((nextoffset >= 0)
- || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
- return -FDT_ERR_NOTFOUND;
- else
- return nextoffset;
- }
- } while (tag != FDT_BEGIN_NODE);
-
- return offset;
-}
-
-int fdt_first_subnode(const void *fdt, int offset)
-{
- int depth = 0;
-
- offset = fdt_next_node(fdt, offset, &depth);
- if (offset < 0 || depth != 1)
- return -FDT_ERR_NOTFOUND;
-
- return offset;
-}
-
-int fdt_next_subnode(const void *fdt, int offset)
-{
- int depth = 1;
-
- /*
- * With respect to the parent, the depth of the next subnode will be
- * the same as the last.
- */
- do {
- offset = fdt_next_node(fdt, offset, &depth);
- if (offset < 0 || depth < 1)
- return -FDT_ERR_NOTFOUND;
- } while (depth > 1);
-
- return offset;
-}
-
-const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
- int slen)
-{
- const char *last = strtab + tabsize - (slen + 1);
- const char *p;
-
- for (p = strtab; p <= last; p++)
- if (memcmp(p, s, slen) == 0 && p[slen] == '\0')
- return p;
- return NULL;
-}
-
-int fdt_move(const void *fdt, void *buf, int bufsize)
-{
- if (!can_assume(VALID_INPUT) && bufsize < 0)
- return -FDT_ERR_NOSPACE;
-
- FDT_RO_PROBE(fdt);
-
- if (fdt_totalsize(fdt) > (unsigned int)bufsize)
- return -FDT_ERR_NOSPACE;
-
- memmove(buf, fdt, fdt_totalsize(fdt));
- return 0;
-}
diff --git a/na/scripts/dtc/libfdt/fdt.h b/na/scripts/dtc/libfdt/fdt.h
deleted file mode 100644
index a07abfc..0000000
--- a/na/scripts/dtc/libfdt/fdt.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
-#ifndef FDT_H
-#define FDT_H
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * Copyright 2012 Kim Phillips, Freescale Semiconductor.
- */
-
-#ifndef __ASSEMBLER__
-
-struct fdt_header {
- fdt32_t magic; /* magic word FDT_MAGIC */
- fdt32_t totalsize; /* total size of DT block */
- fdt32_t off_dt_struct; /* offset to structure */
- fdt32_t off_dt_strings; /* offset to strings */
- fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
- fdt32_t version; /* format version */
- fdt32_t last_comp_version; /* last compatible version */
-
- /* version 2 fields below */
- fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
- booting on */
- /* version 3 fields below */
- fdt32_t size_dt_strings; /* size of the strings block */
-
- /* version 17 fields below */
- fdt32_t size_dt_struct; /* size of the structure block */
-};
-
-struct fdt_reserve_entry {
- fdt64_t address;
- fdt64_t size;
-};
-
-struct fdt_node_header {
- fdt32_t tag;
- char name[];
-};
-
-struct fdt_property {
- fdt32_t tag;
- fdt32_t len;
- fdt32_t nameoff;
- char data[];
-};
-
-#endif /* !__ASSEMBLER__ */
-
-#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
-#define FDT_TAGSIZE sizeof(fdt32_t)
-
-#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
-#define FDT_END_NODE 0x2 /* End node */
-#define FDT_PROP 0x3 /* Property: name off,
- size, content */
-#define FDT_NOP 0x4 /* nop */
-#define FDT_END 0x9
-
-#define FDT_V1_SIZE (7*sizeof(fdt32_t))
-#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
-#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
-#define FDT_V16_SIZE FDT_V3_SIZE
-#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
-
-#endif /* FDT_H */
diff --git a/na/scripts/dtc/libfdt/fdt_addresses.c b/na/scripts/dtc/libfdt/fdt_addresses.c
deleted file mode 100644
index c40ba09..0000000
--- a/na/scripts/dtc/libfdt/fdt_addresses.c
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
- * Copyright (C) 2018 embedded brains GmbH
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-static int fdt_cells(const void *fdt, int nodeoffset, const char *name)
-{
- const fdt32_t *c;
- uint32_t val;
- int len;
-
- c = fdt_getprop(fdt, nodeoffset, name, &len);
- if (!c)
- return len;
-
- if (len != sizeof(*c))
- return -FDT_ERR_BADNCELLS;
-
- val = fdt32_to_cpu(*c);
- if (val > FDT_MAX_NCELLS)
- return -FDT_ERR_BADNCELLS;
-
- return (int)val;
-}
-
-int fdt_address_cells(const void *fdt, int nodeoffset)
-{
- int val;
-
- val = fdt_cells(fdt, nodeoffset, "#address-cells");
- if (val == 0)
- return -FDT_ERR_BADNCELLS;
- if (val == -FDT_ERR_NOTFOUND)
- return 2;
- return val;
-}
-
-int fdt_size_cells(const void *fdt, int nodeoffset)
-{
- int val;
-
- val = fdt_cells(fdt, nodeoffset, "#size-cells");
- if (val == -FDT_ERR_NOTFOUND)
- return 1;
- return val;
-}
-
-/* This function assumes that [address|size]_cells is 1 or 2 */
-int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
- const char *name, uint64_t addr, uint64_t size)
-{
- int addr_cells, size_cells, ret;
- uint8_t data[sizeof(fdt64_t) * 2], *prop;
-
- ret = fdt_address_cells(fdt, parent);
- if (ret < 0)
- return ret;
- addr_cells = ret;
-
- ret = fdt_size_cells(fdt, parent);
- if (ret < 0)
- return ret;
- size_cells = ret;
-
- /* check validity of address */
- prop = data;
- if (addr_cells == 1) {
- if ((addr > UINT32_MAX) || (((uint64_t) UINT32_MAX + 1 - addr) < size))
- return -FDT_ERR_BADVALUE;
-
- fdt32_st(prop, (uint32_t)addr);
- } else if (addr_cells == 2) {
- fdt64_st(prop, addr);
- } else {
- return -FDT_ERR_BADNCELLS;
- }
-
- /* check validity of size */
- prop += addr_cells * sizeof(fdt32_t);
- if (size_cells == 1) {
- if (size > UINT32_MAX)
- return -FDT_ERR_BADVALUE;
-
- fdt32_st(prop, (uint32_t)size);
- } else if (size_cells == 2) {
- fdt64_st(prop, size);
- } else {
- return -FDT_ERR_BADNCELLS;
- }
-
- return fdt_appendprop(fdt, nodeoffset, name, data,
- (addr_cells + size_cells) * sizeof(fdt32_t));
-}
diff --git a/na/scripts/dtc/libfdt/fdt_empty_tree.c b/na/scripts/dtc/libfdt/fdt_empty_tree.c
deleted file mode 100644
index 49d54d4..0000000
--- a/na/scripts/dtc/libfdt/fdt_empty_tree.c
+++ /dev/null
@@ -1,38 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2012 David Gibson, IBM Corporation.
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-int fdt_create_empty_tree(void *buf, int bufsize)
-{
- int err;
-
- err = fdt_create(buf, bufsize);
- if (err)
- return err;
-
- err = fdt_finish_reservemap(buf);
- if (err)
- return err;
-
- err = fdt_begin_node(buf, "");
- if (err)
- return err;
-
- err = fdt_end_node(buf);
- if (err)
- return err;
-
- err = fdt_finish(buf);
- if (err)
- return err;
-
- return fdt_open_into(buf, buf, bufsize);
-}
diff --git a/na/scripts/dtc/libfdt/fdt_overlay.c b/na/scripts/dtc/libfdt/fdt_overlay.c
deleted file mode 100644
index 51a3859..0000000
--- a/na/scripts/dtc/libfdt/fdt_overlay.c
+++ /dev/null
@@ -1,1101 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2016 Free Electrons
- * Copyright (C) 2016 NextThing Co.
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-/**
- * overlay_get_target_phandle - retrieves the target phandle of a fragment
- * @fdto: pointer to the device tree overlay blob
- * @fragment: node offset of the fragment in the overlay
- *
- * overlay_get_target_phandle() retrieves the target phandle of an
- * overlay fragment when that fragment uses a phandle (target
- * property) instead of a path (target-path property).
- *
- * returns:
- * the phandle pointed by the target property
- * 0, if the phandle was not found
- * -1, if the phandle was malformed
- */
-static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
-{
- const fdt32_t *val;
- int len;
-
- val = fdt_getprop(fdto, fragment, "target", &len);
- if (!val)
- return 0;
-
- if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
- return (uint32_t)-1;
-
- return fdt32_to_cpu(*val);
-}
-
-int fdt_overlay_target_offset(const void *fdt, const void *fdto,
- int fragment_offset, char const **pathp)
-{
- uint32_t phandle;
- const char *path = NULL;
- int path_len = 0, ret;
-
- /* Try first to do a phandle based lookup */
- phandle = overlay_get_target_phandle(fdto, fragment_offset);
- if (phandle == (uint32_t)-1)
- return -FDT_ERR_BADPHANDLE;
-
- /* no phandle, try path */
- if (!phandle) {
- /* And then a path based lookup */
- path = fdt_getprop(fdto, fragment_offset, "target-path", &path_len);
- if (path)
- ret = fdt_path_offset(fdt, path);
- else
- ret = path_len;
- } else
- ret = fdt_node_offset_by_phandle(fdt, phandle);
-
- /*
- * If we haven't found either a target or a
- * target-path property in a node that contains a
- * __overlay__ subnode (we wouldn't be called
- * otherwise), consider it a improperly written
- * overlay
- */
- if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
- ret = -FDT_ERR_BADOVERLAY;
-
- /* return on error */
- if (ret < 0)
- return ret;
-
- /* return pointer to path (if available) */
- if (pathp)
- *pathp = path ? path : NULL;
-
- return ret;
-}
-
-/**
- * overlay_phandle_add_offset - Increases a phandle by an offset
- * @fdt: Base device tree blob
- * @node: Device tree overlay blob
- * @name: Name of the property to modify (phandle or linux,phandle)
- * @delta: offset to apply
- *
- * overlay_phandle_add_offset() increments a node phandle by a given
- * offset.
- *
- * returns:
- * 0 on success.
- * Negative error code on error
- */
-static int overlay_phandle_add_offset(void *fdt, int node,
- const char *name, uint32_t delta)
-{
- fdt32_t *valp, val;
- int len;
-
- valp = fdt_getprop_w(fdt, node, name, &len);
- if (!valp)
- return len;
-
- if (len != sizeof(val))
- return -FDT_ERR_BADPHANDLE;
-
- val = fdt32_ld(valp);
- if (val + delta < val || val + delta == (uint32_t)-1)
- return -FDT_ERR_NOPHANDLES;
-
- fdt32_st(valp, val + delta);
- return 0;
-}
-
-/**
- * overlay_adjust_node_phandles - Offsets the phandles of a node
- * @fdto: Device tree overlay blob
- * @node: Offset of the node we want to adjust
- * @delta: Offset to shift the phandles of
- *
- * overlay_adjust_node_phandles() adds a constant to all the phandles
- * of a given node. This is mainly use as part of the overlay
- * application process, when we want to update all the overlay
- * phandles to not conflict with the overlays of the base device tree.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_adjust_node_phandles(void *fdto, int node,
- uint32_t delta)
-{
- int child;
- int ret;
-
- ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
- if (ret && ret != -FDT_ERR_NOTFOUND)
- return ret;
-
- ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
- if (ret && ret != -FDT_ERR_NOTFOUND)
- return ret;
-
- fdt_for_each_subnode(child, fdto, node) {
- ret = overlay_adjust_node_phandles(fdto, child, delta);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
- * @fdto: Device tree overlay blob
- * @delta: Offset to shift the phandles of
- *
- * overlay_adjust_local_phandles() adds a constant to all the
- * phandles of an overlay. This is mainly use as part of the overlay
- * application process, when we want to update all the overlay
- * phandles to not conflict with the overlays of the base device tree.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
-{
- /*
- * Start adjusting the phandles from the overlay root
- */
- return overlay_adjust_node_phandles(fdto, 0, delta);
-}
-
-/**
- * overlay_update_local_node_references - Adjust the overlay references
- * @fdto: Device tree overlay blob
- * @tree_node: Node offset of the node to operate on
- * @fixup_node: Node offset of the matching local fixups node
- * @delta: Offset to shift the phandles of
- *
- * overlay_update_local_nodes_references() update the phandles
- * pointing to a node within the device tree overlay by adding a
- * constant delta.
- *
- * This is mainly used as part of a device tree application process,
- * where you want the device tree overlays phandles to not conflict
- * with the ones from the base device tree before merging them.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_update_local_node_references(void *fdto,
- int tree_node,
- int fixup_node,
- uint32_t delta)
-{
- int fixup_prop;
- int fixup_child;
- int ret;
-
- fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
- const fdt32_t *fixup_val;
- const char *name;
- char *tree_val;
- int fixup_len;
- int tree_len;
- int i;
-
- fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
- &name, &fixup_len);
- if (!fixup_val)
- return fixup_len;
-
- if (fixup_len % sizeof(uint32_t))
- return -FDT_ERR_BADOVERLAY;
- fixup_len /= sizeof(uint32_t);
-
- tree_val = fdt_getprop_w(fdto, tree_node, name, &tree_len);
- if (!tree_val) {
- if (tree_len == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_BADOVERLAY;
-
- return tree_len;
- }
-
- for (i = 0; i < fixup_len; i++) {
- fdt32_t *refp;
-
- refp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));
-
- /*
- * phandles to fixup can be unaligned, so use
- * fdt32_{ld,st}() to read/write them.
- */
- fdt32_st(refp, fdt32_ld(refp) + delta);
- }
- }
-
- fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
- const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
- NULL);
- int tree_child;
-
- tree_child = fdt_subnode_offset(fdto, tree_node,
- fixup_child_name);
- if (tree_child == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_BADOVERLAY;
- if (tree_child < 0)
- return tree_child;
-
- ret = overlay_update_local_node_references(fdto,
- tree_child,
- fixup_child,
- delta);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_update_local_references - Adjust the overlay references
- * @fdto: Device tree overlay blob
- * @delta: Offset to shift the phandles of
- *
- * overlay_update_local_references() update all the phandles pointing
- * to a node within the device tree overlay by adding a constant
- * delta to not conflict with the base overlay.
- *
- * This is mainly used as part of a device tree application process,
- * where you want the device tree overlays phandles to not conflict
- * with the ones from the base device tree before merging them.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_update_local_references(void *fdto, uint32_t delta)
-{
- int fixups;
-
- fixups = fdt_path_offset(fdto, "/__local_fixups__");
- if (fixups < 0) {
- /* There's no local phandles to adjust, bail out */
- if (fixups == -FDT_ERR_NOTFOUND)
- return 0;
-
- return fixups;
- }
-
- /*
- * Update our local references from the root of the tree
- */
- return overlay_update_local_node_references(fdto, 0, fixups,
- delta);
-}
-
-/**
- * overlay_fixup_one_phandle - Set an overlay phandle to the base one
- * @fdto: Device tree overlay blob
- * @symbols_off: Node offset of the symbols node in the base device tree
- * @path: Path to a node holding a phandle in the overlay
- * @path_len: number of path characters to consider
- * @name: Name of the property holding the phandle reference in the overlay
- * @name_len: number of name characters to consider
- * @poffset: Offset within the overlay property where the phandle is stored
- * @phandle: Phandle referencing the node
- *
- * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
- * a node in the base device tree.
- *
- * This is part of the device tree overlay application process, when
- * you want all the phandles in the overlay to point to the actual
- * base dt nodes.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_fixup_one_phandle(void *fdto, int symbols_off,
- const char *path, uint32_t path_len,
- const char *name, uint32_t name_len,
- int poffset, uint32_t phandle)
-{
- fdt32_t phandle_prop;
- int fixup_off;
-
- if (symbols_off < 0)
- return symbols_off;
-
- fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
- if (fixup_off == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_BADOVERLAY;
- if (fixup_off < 0)
- return fixup_off;
-
- phandle_prop = cpu_to_fdt32(phandle);
- return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
- name, name_len, poffset,
- &phandle_prop,
- sizeof(phandle_prop));
-}
-
-/**
- * overlay_fixup_phandle - Set an overlay phandle to the base one
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- * @symbols_off: Node offset of the symbols node in the base device tree
- * @property: Property offset in the overlay holding the list of fixups
- *
- * overlay_fixup_phandle() resolves all the overlay phandles pointed
- * to in a __fixups__ property, and updates them to match the phandles
- * in use in the base device tree.
- *
- * This is part of the device tree overlay application process, when
- * you want all the phandles in the overlay to point to the actual
- * base dt nodes.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
- int property)
-{
- const char *value;
- const char *label;
- int len;
- const char *symbol_path;
- int prop_len;
- int symbol_off;
- uint32_t phandle;
-
- value = fdt_getprop_by_offset(fdto, property,
- &label, &len);
- if (!value) {
- if (len == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_INTERNAL;
-
- return len;
- }
-
- symbol_path = fdt_getprop(fdt, symbols_off, label, &prop_len);
- if (!symbol_path)
- return prop_len;
-
- symbol_off = fdt_path_offset(fdt, symbol_path);
- if (symbol_off < 0)
- return symbol_off;
-
- phandle = fdt_get_phandle(fdt, symbol_off);
- if (!phandle)
- return -FDT_ERR_NOTFOUND;
-
- do {
- const char *path, *name, *fixup_end;
- const char *fixup_str = value;
- uint32_t path_len, name_len;
- uint32_t fixup_len;
- const char *sep;
- char *endptr;
- int poffset, ret;
-
- fixup_end = memchr(value, '\0', len);
- if (!fixup_end)
- return -FDT_ERR_BADOVERLAY;
- fixup_len = fixup_end - fixup_str;
-
- len -= fixup_len + 1;
- value += fixup_len + 1;
-
- path = fixup_str;
- sep = memchr(fixup_str, ':', fixup_len);
- if (!sep || *sep != ':')
- return -FDT_ERR_BADOVERLAY;
-
- path_len = sep - path;
- if (path_len == (fixup_len - 1))
- return -FDT_ERR_BADOVERLAY;
-
- fixup_len -= path_len + 1;
- name = sep + 1;
- sep = memchr(name, ':', fixup_len);
- if (!sep || *sep != ':')
- return -FDT_ERR_BADOVERLAY;
-
- name_len = sep - name;
- if (!name_len)
- return -FDT_ERR_BADOVERLAY;
-
- poffset = strtoul(sep + 1, &endptr, 10);
- if ((*endptr != '\0') || (endptr <= (sep + 1)))
- return -FDT_ERR_BADOVERLAY;
-
- ret = overlay_fixup_one_phandle(fdto, symbols_off,
- path, path_len, name, name_len,
- poffset, phandle);
- if (ret)
- return ret;
- } while (len > 0);
-
- return 0;
-}
-
-/**
- * overlay_fixup_phandles - Resolve the overlay phandles to the base
- * device tree
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- *
- * overlay_fixup_phandles() resolves all the overlay phandles pointing
- * to nodes in the base device tree.
- *
- * This is one of the steps of the device tree overlay application
- * process, when you want all the phandles in the overlay to point to
- * the actual base dt nodes.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_fixup_phandles(void *fdt, void *fdto)
-{
- int fixups_off, symbols_off;
- int property;
-
- /* We can have overlays without any fixups */
- fixups_off = fdt_path_offset(fdto, "/__fixups__");
- if (fixups_off == -FDT_ERR_NOTFOUND)
- return 0; /* nothing to do */
- if (fixups_off < 0)
- return fixups_off;
-
- /* And base DTs without symbols */
- symbols_off = fdt_path_offset(fdt, "/__symbols__");
- if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
- return symbols_off;
-
- fdt_for_each_property_offset(property, fdto, fixups_off) {
- int ret;
-
- ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_adjust_local_conflicting_phandle: Changes a phandle value
- * @fdto: Device tree overlay
- * @node: The node the phandle is set for
- * @fdt_phandle: The new value for the phandle
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_adjust_local_conflicting_phandle(void *fdto, int node,
- uint32_t fdt_phandle)
-{
- const fdt32_t *php;
- int len, ret;
-
- php = fdt_getprop(fdto, node, "phandle", &len);
- if (php && len == sizeof(*php)) {
- ret = fdt_setprop_inplace_u32(fdto, node, "phandle", fdt_phandle);
- if (ret)
- return ret;
- }
-
- php = fdt_getprop(fdto, node, "linux,phandle", &len);
- if (php && len == sizeof(*php)) {
- ret = fdt_setprop_inplace_u32(fdto, node, "linux,phandle", fdt_phandle);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_update_node_conflicting_references - Recursively replace phandle values
- * @fdto: Device tree overlay blob
- * @tree_node: Node to recurse into
- * @fixup_node: Node offset of the matching local fixups node
- * @fdt_phandle: Value to replace phandles with
- * @fdto_phandle: Value to be replaced
- *
- * Replaces all phandles with value @fdto_phandle by @fdt_phandle.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_update_node_conflicting_references(void *fdto, int tree_node,
- int fixup_node,
- uint32_t fdt_phandle,
- uint32_t fdto_phandle)
-{
- int fixup_prop;
- int fixup_child;
- int ret;
-
- fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
- const fdt32_t *fixup_val;
- const char *name;
- char *tree_val;
- int fixup_len;
- int tree_len;
- int i;
-
- fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
- &name, &fixup_len);
- if (!fixup_val)
- return fixup_len;
-
- if (fixup_len % sizeof(uint32_t))
- return -FDT_ERR_BADOVERLAY;
- fixup_len /= sizeof(uint32_t);
-
- tree_val = fdt_getprop_w(fdto, tree_node, name, &tree_len);
- if (!tree_val) {
- if (tree_len == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_BADOVERLAY;
-
- return tree_len;
- }
-
- for (i = 0; i < fixup_len; i++) {
- fdt32_t *refp;
- uint32_t valp;
-
- refp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));
- valp = fdt32_ld(refp);
-
- if (valp == fdto_phandle)
- fdt32_st(refp, fdt_phandle);
- }
- }
-
- fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
- const char *fixup_child_name = fdt_get_name(fdto, fixup_child, NULL);
- int tree_child;
-
- tree_child = fdt_subnode_offset(fdto, tree_node, fixup_child_name);
-
- if (tree_child == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_BADOVERLAY;
- if (tree_child < 0)
- return tree_child;
-
- ret = overlay_update_node_conflicting_references(fdto, tree_child,
- fixup_child,
- fdt_phandle,
- fdto_phandle);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_update_local_conflicting_references - Recursively replace phandle values
- * @fdto: Device tree overlay blob
- * @fdt_phandle: Value to replace phandles with
- * @fdto_phandle: Value to be replaced
- *
- * Replaces all phandles with value @fdto_phandle by @fdt_phandle.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_update_local_conflicting_references(void *fdto,
- uint32_t fdt_phandle,
- uint32_t fdto_phandle)
-{
- int fixups;
-
- fixups = fdt_path_offset(fdto, "/__local_fixups__");
- if (fixups == -FDT_ERR_NOTFOUND)
- return 0;
- if (fixups < 0)
- return fixups;
-
- return overlay_update_node_conflicting_references(fdto, 0, fixups,
- fdt_phandle,
- fdto_phandle);
-}
-
-/**
- * overlay_prevent_phandle_overwrite_node - Helper function for overlay_prevent_phandle_overwrite
- * @fdt: Base Device tree blob
- * @fdtnode: Node in fdt that is checked for an overwrite
- * @fdto: Device tree overlay blob
- * @fdtonode: Node in fdto matching @fdtnode
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_prevent_phandle_overwrite_node(void *fdt, int fdtnode,
- void *fdto, int fdtonode)
-{
- uint32_t fdt_phandle, fdto_phandle;
- int fdtochild;
-
- fdt_phandle = fdt_get_phandle(fdt, fdtnode);
- fdto_phandle = fdt_get_phandle(fdto, fdtonode);
-
- if (fdt_phandle && fdto_phandle) {
- int ret;
-
- ret = overlay_adjust_local_conflicting_phandle(fdto, fdtonode,
- fdt_phandle);
- if (ret)
- return ret;
-
- ret = overlay_update_local_conflicting_references(fdto,
- fdt_phandle,
- fdto_phandle);
- if (ret)
- return ret;
- }
-
- fdt_for_each_subnode(fdtochild, fdto, fdtonode) {
- const char *name = fdt_get_name(fdto, fdtochild, NULL);
- int fdtchild;
- int ret;
-
- fdtchild = fdt_subnode_offset(fdt, fdtnode, name);
- if (fdtchild == -FDT_ERR_NOTFOUND)
- /*
- * no further overwrites possible here as this node is
- * new
- */
- continue;
-
- ret = overlay_prevent_phandle_overwrite_node(fdt, fdtchild,
- fdto, fdtochild);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_prevent_phandle_overwrite - Fixes overlay phandles to not overwrite base phandles
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- *
- * Checks recursively if applying fdto overwrites phandle values in the base
- * dtb. When such a phandle is found, the fdto is changed to use the fdt's
- * phandle value to not break references in the base.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_prevent_phandle_overwrite(void *fdt, void *fdto)
-{
- int fragment;
-
- fdt_for_each_subnode(fragment, fdto, 0) {
- int overlay;
- int target;
- int ret;
-
- overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
- if (overlay == -FDT_ERR_NOTFOUND)
- continue;
-
- if (overlay < 0)
- return overlay;
-
- target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL);
- if (target == -FDT_ERR_NOTFOUND)
- /*
- * The subtree doesn't exist in the base, so nothing
- * will be overwritten.
- */
- continue;
- else if (target < 0)
- return target;
-
- ret = overlay_prevent_phandle_overwrite_node(fdt, target,
- fdto, overlay);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_apply_node - Merges a node into the base device tree
- * @fdt: Base Device Tree blob
- * @target: Node offset in the base device tree to apply the fragment to
- * @fdto: Device tree overlay blob
- * @node: Node offset in the overlay holding the changes to merge
- *
- * overlay_apply_node() merges a node into a target base device tree
- * node pointed.
- *
- * This is part of the final step in the device tree overlay
- * application process, when all the phandles have been adjusted and
- * resolved and you just have to merge overlay into the base device
- * tree.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_apply_node(void *fdt, int target,
- void *fdto, int node)
-{
- int property;
- int subnode;
-
- fdt_for_each_property_offset(property, fdto, node) {
- const char *name;
- const void *prop;
- int prop_len;
- int ret;
-
- prop = fdt_getprop_by_offset(fdto, property, &name,
- &prop_len);
- if (prop_len == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_INTERNAL;
- if (prop_len < 0)
- return prop_len;
-
- ret = fdt_setprop(fdt, target, name, prop, prop_len);
- if (ret)
- return ret;
- }
-
- fdt_for_each_subnode(subnode, fdto, node) {
- const char *name = fdt_get_name(fdto, subnode, NULL);
- int nnode;
- int ret;
-
- nnode = fdt_add_subnode(fdt, target, name);
- if (nnode == -FDT_ERR_EXISTS) {
- nnode = fdt_subnode_offset(fdt, target, name);
- if (nnode == -FDT_ERR_NOTFOUND)
- return -FDT_ERR_INTERNAL;
- }
-
- if (nnode < 0)
- return nnode;
-
- ret = overlay_apply_node(fdt, nnode, fdto, subnode);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * overlay_merge - Merge an overlay into its base device tree
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- *
- * overlay_merge() merges an overlay into its base device tree.
- *
- * This is the next to last step in the device tree overlay application
- * process, when all the phandles have been adjusted and resolved and
- * you just have to merge overlay into the base device tree.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_merge(void *fdt, void *fdto)
-{
- int fragment;
-
- fdt_for_each_subnode(fragment, fdto, 0) {
- int overlay;
- int target;
- int ret;
-
- /*
- * Each fragments will have an __overlay__ node. If
- * they don't, it's not supposed to be merged
- */
- overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
- if (overlay == -FDT_ERR_NOTFOUND)
- continue;
-
- if (overlay < 0)
- return overlay;
-
- target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL);
- if (target < 0)
- return target;
-
- ret = overlay_apply_node(fdt, target, fdto, overlay);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int get_path_len(const void *fdt, int nodeoffset)
-{
- int len = 0, namelen;
- const char *name;
-
- FDT_RO_PROBE(fdt);
-
- for (;;) {
- name = fdt_get_name(fdt, nodeoffset, &namelen);
- if (!name)
- return namelen;
-
- /* root? we're done */
- if (namelen == 0)
- break;
-
- nodeoffset = fdt_parent_offset(fdt, nodeoffset);
- if (nodeoffset < 0)
- return nodeoffset;
- len += namelen + 1;
- }
-
- /* in case of root pretend it's "/" */
- if (len == 0)
- len++;
- return len;
-}
-
-/**
- * overlay_symbol_update - Update the symbols of base tree after a merge
- * @fdt: Base Device Tree blob
- * @fdto: Device tree overlay blob
- *
- * overlay_symbol_update() updates the symbols of the base tree with the
- * symbols of the applied overlay
- *
- * This is the last step in the device tree overlay application
- * process, allowing the reference of overlay symbols by subsequent
- * overlay operations.
- *
- * returns:
- * 0 on success
- * Negative error code on failure
- */
-static int overlay_symbol_update(void *fdt, void *fdto)
-{
- int root_sym, ov_sym, prop, path_len, fragment, target;
- int len, frag_name_len, ret, rel_path_len;
- const char *s, *e;
- const char *path;
- const char *name;
- const char *frag_name;
- const char *rel_path;
- const char *target_path;
- char *buf;
- void *p;
-
- ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
-
- /* if no overlay symbols exist no problem */
- if (ov_sym < 0)
- return 0;
-
- root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
-
- /* it no root symbols exist we should create them */
- if (root_sym == -FDT_ERR_NOTFOUND)
- root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
-
- /* any error is fatal now */
- if (root_sym < 0)
- return root_sym;
-
- /* iterate over each overlay symbol */
- fdt_for_each_property_offset(prop, fdto, ov_sym) {
- path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
- if (!path)
- return path_len;
-
- /* verify it's a string property (terminated by a single \0) */
- if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
- return -FDT_ERR_BADVALUE;
-
- /* keep end marker to avoid strlen() */
- e = path + path_len;
-
- if (*path != '/')
- return -FDT_ERR_BADVALUE;
-
- /* get fragment name first */
- s = strchr(path + 1, '/');
- if (!s) {
- /* Symbol refers to something that won't end
- * up in the target tree */
- continue;
- }
-
- frag_name = path + 1;
- frag_name_len = s - path - 1;
-
- /* verify format; safe since "s" lies in \0 terminated prop */
- len = sizeof("/__overlay__/") - 1;
- if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) {
- /* /<fragment-name>/__overlay__/<relative-subnode-path> */
- rel_path = s + len;
- rel_path_len = e - rel_path - 1;
- } else if ((e - s) == len
- && (memcmp(s, "/__overlay__", len - 1) == 0)) {
- /* /<fragment-name>/__overlay__ */
- rel_path = "";
- rel_path_len = 0;
- } else {
- /* Symbol refers to something that won't end
- * up in the target tree */
- continue;
- }
-
- /* find the fragment index in which the symbol lies */
- ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
- frag_name_len);
- /* not found? */
- if (ret < 0)
- return -FDT_ERR_BADOVERLAY;
- fragment = ret;
-
- /* an __overlay__ subnode must exist */
- ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
- if (ret < 0)
- return -FDT_ERR_BADOVERLAY;
-
- /* get the target of the fragment */
- ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path);
- if (ret < 0)
- return ret;
- target = ret;
-
- /* if we have a target path use */
- if (!target_path) {
- ret = get_path_len(fdt, target);
- if (ret < 0)
- return ret;
- len = ret;
- } else {
- len = strlen(target_path);
- }
-
- ret = fdt_setprop_placeholder(fdt, root_sym, name,
- len + (len > 1) + rel_path_len + 1, &p);
- if (ret < 0)
- return ret;
-
- if (!target_path) {
- /* again in case setprop_placeholder changed it */
- ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path);
- if (ret < 0)
- return ret;
- target = ret;
- }
-
- buf = p;
- if (len > 1) { /* target is not root */
- if (!target_path) {
- ret = fdt_get_path(fdt, target, buf, len + 1);
- if (ret < 0)
- return ret;
- } else
- memcpy(buf, target_path, len + 1);
-
- } else
- len--;
-
- buf[len] = '/';
- memcpy(buf + len + 1, rel_path, rel_path_len);
- buf[len + 1 + rel_path_len] = '\0';
- }
-
- return 0;
-}
-
-int fdt_overlay_apply(void *fdt, void *fdto)
-{
- uint32_t delta;
- int ret;
-
- FDT_RO_PROBE(fdt);
- FDT_RO_PROBE(fdto);
-
- ret = fdt_find_max_phandle(fdt, &delta);
- if (ret)
- goto err;
-
- /* Increase all phandles in the fdto by delta */
- ret = overlay_adjust_local_phandles(fdto, delta);
- if (ret)
- goto err;
-
- /* Adapt the phandle values in fdto to the above increase */
- ret = overlay_update_local_references(fdto, delta);
- if (ret)
- goto err;
-
- /* Update fdto's phandles using symbols from fdt */
- ret = overlay_fixup_phandles(fdt, fdto);
- if (ret)
- goto err;
-
- /* Don't overwrite phandles in fdt */
- ret = overlay_prevent_phandle_overwrite(fdt, fdto);
- if (ret)
- goto err;
-
- ret = overlay_merge(fdt, fdto);
- if (ret)
- goto err;
-
- ret = overlay_symbol_update(fdt, fdto);
- if (ret)
- goto err;
-
- /*
- * The overlay has been damaged, erase its magic.
- */
- fdt_set_magic(fdto, ~0);
-
- return 0;
-
-err:
- /*
- * The overlay might have been damaged, erase its magic.
- */
- fdt_set_magic(fdto, ~0);
-
- /*
- * The base device tree might have been damaged, erase its
- * magic.
- */
- fdt_set_magic(fdt, ~0);
-
- return ret;
-}
diff --git a/na/scripts/dtc/libfdt/fdt_ro.c b/na/scripts/dtc/libfdt/fdt_ro.c
deleted file mode 100644
index 63494fb..0000000
--- a/na/scripts/dtc/libfdt/fdt_ro.c
+++ /dev/null
@@ -1,888 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-static int fdt_nodename_eq_(const void *fdt, int offset,
- const char *s, int len)
-{
- int olen;
- const char *p = fdt_get_name(fdt, offset, &olen);
-
- if (!p || olen < len)
- /* short match */
- return 0;
-
- if (memcmp(p, s, len) != 0)
- return 0;
-
- if (p[len] == '\0')
- return 1;
- else if (!memchr(s, '@', len) && (p[len] == '@'))
- return 1;
- else
- return 0;
-}
-
-const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
-{
- int32_t totalsize;
- uint32_t absoffset;
- size_t len;
- int err;
- const char *s, *n;
-
- if (can_assume(VALID_INPUT)) {
- s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
-
- if (lenp)
- *lenp = strlen(s);
- return s;
- }
- totalsize = fdt_ro_probe_(fdt);
- err = totalsize;
- if (totalsize < 0)
- goto fail;
-
- err = -FDT_ERR_BADOFFSET;
- absoffset = stroffset + fdt_off_dt_strings(fdt);
- if (absoffset >= (unsigned)totalsize)
- goto fail;
- len = totalsize - absoffset;
-
- if (fdt_magic(fdt) == FDT_MAGIC) {
- if (stroffset < 0)
- goto fail;
- if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
- if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
- goto fail;
- if ((fdt_size_dt_strings(fdt) - stroffset) < len)
- len = fdt_size_dt_strings(fdt) - stroffset;
- }
- } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
- unsigned int sw_stroffset = -stroffset;
-
- if ((stroffset >= 0) ||
- (sw_stroffset > fdt_size_dt_strings(fdt)))
- goto fail;
- if (sw_stroffset < len)
- len = sw_stroffset;
- } else {
- err = -FDT_ERR_INTERNAL;
- goto fail;
- }
-
- s = (const char *)fdt + absoffset;
- n = memchr(s, '\0', len);
- if (!n) {
- /* missing terminating NULL */
- err = -FDT_ERR_TRUNCATED;
- goto fail;
- }
-
- if (lenp)
- *lenp = n - s;
- return s;
-
-fail:
- if (lenp)
- *lenp = err;
- return NULL;
-}
-
-const char *fdt_string(const void *fdt, int stroffset)
-{
- return fdt_get_string(fdt, stroffset, NULL);
-}
-
-static int fdt_string_eq_(const void *fdt, int stroffset,
- const char *s, int len)
-{
- int slen;
- const char *p = fdt_get_string(fdt, stroffset, &slen);
-
- return p && (slen == len) && (memcmp(p, s, len) == 0);
-}
-
-int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
-{
- uint32_t max = 0;
- int offset = -1;
-
- while (true) {
- uint32_t value;
-
- offset = fdt_next_node(fdt, offset, NULL);
- if (offset < 0) {
- if (offset == -FDT_ERR_NOTFOUND)
- break;
-
- return offset;
- }
-
- value = fdt_get_phandle(fdt, offset);
-
- if (value > max)
- max = value;
- }
-
- if (phandle)
- *phandle = max;
-
- return 0;
-}
-
-int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
-{
- uint32_t max;
- int err;
-
- err = fdt_find_max_phandle(fdt, &max);
- if (err < 0)
- return err;
-
- if (max == FDT_MAX_PHANDLE)
- return -FDT_ERR_NOPHANDLES;
-
- if (phandle)
- *phandle = max + 1;
-
- return 0;
-}
-
-static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
-{
- unsigned int offset = n * sizeof(struct fdt_reserve_entry);
- unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
-
- if (!can_assume(VALID_INPUT)) {
- if (absoffset < fdt_off_mem_rsvmap(fdt))
- return NULL;
- if (absoffset > fdt_totalsize(fdt) -
- sizeof(struct fdt_reserve_entry))
- return NULL;
- }
- return fdt_mem_rsv_(fdt, n);
-}
-
-int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
-{
- const struct fdt_reserve_entry *re;
-
- FDT_RO_PROBE(fdt);
- re = fdt_mem_rsv(fdt, n);
- if (!can_assume(VALID_INPUT) && !re)
- return -FDT_ERR_BADOFFSET;
-
- *address = fdt64_ld_(&re->address);
- *size = fdt64_ld_(&re->size);
- return 0;
-}
-
-int fdt_num_mem_rsv(const void *fdt)
-{
- int i;
- const struct fdt_reserve_entry *re;
-
- for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
- if (fdt64_ld_(&re->size) == 0)
- return i;
- }
- return -FDT_ERR_TRUNCATED;
-}
-
-static int nextprop_(const void *fdt, int offset)
-{
- uint32_t tag;
- int nextoffset;
-
- do {
- tag = fdt_next_tag(fdt, offset, &nextoffset);
-
- switch (tag) {
- case FDT_END:
- if (nextoffset >= 0)
- return -FDT_ERR_BADSTRUCTURE;
- else
- return nextoffset;
-
- case FDT_PROP:
- return offset;
- }
- offset = nextoffset;
- } while (tag == FDT_NOP);
-
- return -FDT_ERR_NOTFOUND;
-}
-
-int fdt_subnode_offset_namelen(const void *fdt, int offset,
- const char *name, int namelen)
-{
- int depth;
-
- FDT_RO_PROBE(fdt);
-
- for (depth = 0;
- (offset >= 0) && (depth >= 0);
- offset = fdt_next_node(fdt, offset, &depth))
- if ((depth == 1)
- && fdt_nodename_eq_(fdt, offset, name, namelen))
- return offset;
-
- if (depth < 0)
- return -FDT_ERR_NOTFOUND;
- return offset; /* error */
-}
-
-int fdt_subnode_offset(const void *fdt, int parentoffset,
- const char *name)
-{
- return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
-}
-
-int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
-{
- const char *end = path + namelen;
- const char *p = path;
- int offset = 0;
-
- FDT_RO_PROBE(fdt);
-
- if (!can_assume(VALID_INPUT) && namelen <= 0)
- return -FDT_ERR_BADPATH;
-
- /* see if we have an alias */
- if (*path != '/') {
- const char *q = memchr(path, '/', end - p);
-
- if (!q)
- q = end;
-
- p = fdt_get_alias_namelen(fdt, p, q - p);
- if (!p)
- return -FDT_ERR_BADPATH;
- offset = fdt_path_offset(fdt, p);
-
- p = q;
- }
-
- while (p < end) {
- const char *q;
-
- while (*p == '/') {
- p++;
- if (p == end)
- return offset;
- }
- q = memchr(p, '/', end - p);
- if (! q)
- q = end;
-
- offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
- if (offset < 0)
- return offset;
-
- p = q;
- }
-
- return offset;
-}
-
-int fdt_path_offset(const void *fdt, const char *path)
-{
- return fdt_path_offset_namelen(fdt, path, strlen(path));
-}
-
-const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
-{
- const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
- const char *nameptr;
- int err;
-
- if (!can_assume(VALID_DTB) && (((err = fdt_ro_probe_(fdt)) < 0)
- || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
- goto fail;
-
- nameptr = nh->name;
-
- if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
- /*
- * For old FDT versions, match the naming conventions of V16:
- * give only the leaf name (after all /). The actual tree
- * contents are loosely checked.
- */
- const char *leaf;
- leaf = strrchr(nameptr, '/');
- if (leaf == NULL) {
- err = -FDT_ERR_BADSTRUCTURE;
- goto fail;
- }
- nameptr = leaf+1;
- }
-
- if (len)
- *len = strlen(nameptr);
-
- return nameptr;
-
- fail:
- if (len)
- *len = err;
- return NULL;
-}
-
-int fdt_first_property_offset(const void *fdt, int nodeoffset)
-{
- int offset;
-
- if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
- return offset;
-
- return nextprop_(fdt, offset);
-}
-
-int fdt_next_property_offset(const void *fdt, int offset)
-{
- if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
- return offset;
-
- return nextprop_(fdt, offset);
-}
-
-static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
- int offset,
- int *lenp)
-{
- int err;
- const struct fdt_property *prop;
-
- if (!can_assume(VALID_INPUT) &&
- (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
- if (lenp)
- *lenp = err;
- return NULL;
- }
-
- prop = fdt_offset_ptr_(fdt, offset);
-
- if (lenp)
- *lenp = fdt32_ld_(&prop->len);
-
- return prop;
-}
-
-const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
- int offset,
- int *lenp)
-{
- /* Prior to version 16, properties may need realignment
- * and this API does not work. fdt_getprop_*() will, however. */
-
- if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
- if (lenp)
- *lenp = -FDT_ERR_BADVERSION;
- return NULL;
- }
-
- return fdt_get_property_by_offset_(fdt, offset, lenp);
-}
-
-static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
- int offset,
- const char *name,
- int namelen,
- int *lenp,
- int *poffset)
-{
- for (offset = fdt_first_property_offset(fdt, offset);
- (offset >= 0);
- (offset = fdt_next_property_offset(fdt, offset))) {
- const struct fdt_property *prop;
-
- prop = fdt_get_property_by_offset_(fdt, offset, lenp);
- if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
- offset = -FDT_ERR_INTERNAL;
- break;
- }
- if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
- name, namelen)) {
- if (poffset)
- *poffset = offset;
- return prop;
- }
- }
-
- if (lenp)
- *lenp = offset;
- return NULL;
-}
-
-
-const struct fdt_property *fdt_get_property_namelen(const void *fdt,
- int offset,
- const char *name,
- int namelen, int *lenp)
-{
- /* Prior to version 16, properties may need realignment
- * and this API does not work. fdt_getprop_*() will, however. */
- if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
- if (lenp)
- *lenp = -FDT_ERR_BADVERSION;
- return NULL;
- }
-
- return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
- NULL);
-}
-
-
-const struct fdt_property *fdt_get_property(const void *fdt,
- int nodeoffset,
- const char *name, int *lenp)
-{
- return fdt_get_property_namelen(fdt, nodeoffset, name,
- strlen(name), lenp);
-}
-
-const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
- const char *name, int namelen, int *lenp)
-{
- int poffset;
- const struct fdt_property *prop;
-
- prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
- &poffset);
- if (!prop)
- return NULL;
-
- /* Handle realignment */
- if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
- (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
- return prop->data + 4;
- return prop->data;
-}
-
-const void *fdt_getprop_by_offset(const void *fdt, int offset,
- const char **namep, int *lenp)
-{
- const struct fdt_property *prop;
-
- prop = fdt_get_property_by_offset_(fdt, offset, lenp);
- if (!prop)
- return NULL;
- if (namep) {
- const char *name;
- int namelen;
-
- if (!can_assume(VALID_INPUT)) {
- name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
- &namelen);
- *namep = name;
- if (!name) {
- if (lenp)
- *lenp = namelen;
- return NULL;
- }
- } else {
- *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
- }
- }
-
- /* Handle realignment */
- if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
- (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
- return prop->data + 4;
- return prop->data;
-}
-
-const void *fdt_getprop(const void *fdt, int nodeoffset,
- const char *name, int *lenp)
-{
- return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
-}
-
-uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
-{
- const fdt32_t *php;
- int len;
-
- /* FIXME: This is a bit sub-optimal, since we potentially scan
- * over all the properties twice. */
- php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
- if (!php || (len != sizeof(*php))) {
- php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
- if (!php || (len != sizeof(*php)))
- return 0;
- }
-
- return fdt32_ld_(php);
-}
-
-static const void *fdt_path_getprop_namelen(const void *fdt, const char *path,
- const char *propname, int propnamelen,
- int *lenp)
-{
- int offset = fdt_path_offset(fdt, path);
-
- if (offset < 0)
- return NULL;
-
- return fdt_getprop_namelen(fdt, offset, propname, propnamelen, lenp);
-}
-
-const char *fdt_get_alias_namelen(const void *fdt,
- const char *name, int namelen)
-{
- int len;
- const char *alias;
-
- alias = fdt_path_getprop_namelen(fdt, "/aliases", name, namelen, &len);
-
- if (!can_assume(VALID_DTB) &&
- !(alias && len > 0 && alias[len - 1] == '\0' && *alias == '/'))
- return NULL;
-
- return alias;
-}
-
-const char *fdt_get_alias(const void *fdt, const char *name)
-{
- return fdt_get_alias_namelen(fdt, name, strlen(name));
-}
-
-const char *fdt_get_symbol_namelen(const void *fdt,
- const char *name, int namelen)
-{
- return fdt_path_getprop_namelen(fdt, "/__symbols__", name, namelen, NULL);
-}
-
-const char *fdt_get_symbol(const void *fdt, const char *name)
-{
- return fdt_get_symbol_namelen(fdt, name, strlen(name));
-}
-
-int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
-{
- int pdepth = 0, p = 0;
- int offset, depth, namelen;
- const char *name;
-
- FDT_RO_PROBE(fdt);
-
- if (buflen < 2)
- return -FDT_ERR_NOSPACE;
-
- for (offset = 0, depth = 0;
- (offset >= 0) && (offset <= nodeoffset);
- offset = fdt_next_node(fdt, offset, &depth)) {
- while (pdepth > depth) {
- do {
- p--;
- } while (buf[p-1] != '/');
- pdepth--;
- }
-
- if (pdepth >= depth) {
- name = fdt_get_name(fdt, offset, &namelen);
- if (!name)
- return namelen;
- if ((p + namelen + 1) <= buflen) {
- memcpy(buf + p, name, namelen);
- p += namelen;
- buf[p++] = '/';
- pdepth++;
- }
- }
-
- if (offset == nodeoffset) {
- if (pdepth < (depth + 1))
- return -FDT_ERR_NOSPACE;
-
- if (p > 1) /* special case so that root path is "/", not "" */
- p--;
- buf[p] = '\0';
- return 0;
- }
- }
-
- if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
- return -FDT_ERR_BADOFFSET;
- else if (offset == -FDT_ERR_BADOFFSET)
- return -FDT_ERR_BADSTRUCTURE;
-
- return offset; /* error from fdt_next_node() */
-}
-
-int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
- int supernodedepth, int *nodedepth)
-{
- int offset, depth;
- int supernodeoffset = -FDT_ERR_INTERNAL;
-
- FDT_RO_PROBE(fdt);
-
- if (supernodedepth < 0)
- return -FDT_ERR_NOTFOUND;
-
- for (offset = 0, depth = 0;
- (offset >= 0) && (offset <= nodeoffset);
- offset = fdt_next_node(fdt, offset, &depth)) {
- if (depth == supernodedepth)
- supernodeoffset = offset;
-
- if (offset == nodeoffset) {
- if (nodedepth)
- *nodedepth = depth;
-
- if (supernodedepth > depth)
- return -FDT_ERR_NOTFOUND;
- else
- return supernodeoffset;
- }
- }
-
- if (!can_assume(VALID_INPUT)) {
- if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
- return -FDT_ERR_BADOFFSET;
- else if (offset == -FDT_ERR_BADOFFSET)
- return -FDT_ERR_BADSTRUCTURE;
- }
-
- return offset; /* error from fdt_next_node() */
-}
-
-int fdt_node_depth(const void *fdt, int nodeoffset)
-{
- int nodedepth;
- int err;
-
- err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
- if (err)
- return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
- -FDT_ERR_INTERNAL;
- return nodedepth;
-}
-
-int fdt_parent_offset(const void *fdt, int nodeoffset)
-{
- int nodedepth = fdt_node_depth(fdt, nodeoffset);
-
- if (nodedepth < 0)
- return nodedepth;
- return fdt_supernode_atdepth_offset(fdt, nodeoffset,
- nodedepth - 1, NULL);
-}
-
-int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
- const char *propname,
- const void *propval, int proplen)
-{
- int offset;
- const void *val;
- int len;
-
- FDT_RO_PROBE(fdt);
-
- /* FIXME: The algorithm here is pretty horrible: we scan each
- * property of a node in fdt_getprop(), then if that didn't
- * find what we want, we scan over them again making our way
- * to the next node. Still it's the easiest to implement
- * approach; performance can come later. */
- for (offset = fdt_next_node(fdt, startoffset, NULL);
- offset >= 0;
- offset = fdt_next_node(fdt, offset, NULL)) {
- val = fdt_getprop(fdt, offset, propname, &len);
- if (val && (len == proplen)
- && (memcmp(val, propval, len) == 0))
- return offset;
- }
-
- return offset; /* error from fdt_next_node() */
-}
-
-int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
-{
- int offset;
-
- if ((phandle == 0) || (phandle == ~0U))
- return -FDT_ERR_BADPHANDLE;
-
- FDT_RO_PROBE(fdt);
-
- /* FIXME: The algorithm here is pretty horrible: we
- * potentially scan each property of a node in
- * fdt_get_phandle(), then if that didn't find what
- * we want, we scan over them again making our way to the next
- * node. Still it's the easiest to implement approach;
- * performance can come later. */
- for (offset = fdt_next_node(fdt, -1, NULL);
- offset >= 0;
- offset = fdt_next_node(fdt, offset, NULL)) {
- if (fdt_get_phandle(fdt, offset) == phandle)
- return offset;
- }
-
- return offset; /* error from fdt_next_node() */
-}
-
-int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
-{
- int len = strlen(str);
- const char *p;
-
- while (listlen >= len) {
- if (memcmp(str, strlist, len+1) == 0)
- return 1;
- p = memchr(strlist, '\0', listlen);
- if (!p)
- return 0; /* malformed strlist.. */
- listlen -= (p-strlist) + 1;
- strlist = p + 1;
- }
- return 0;
-}
-
-int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
-{
- const char *list, *end;
- int length, count = 0;
-
- list = fdt_getprop(fdt, nodeoffset, property, &length);
- if (!list)
- return length;
-
- end = list + length;
-
- while (list < end) {
- length = strnlen(list, end - list) + 1;
-
- /* Abort if the last string isn't properly NUL-terminated. */
- if (list + length > end)
- return -FDT_ERR_BADVALUE;
-
- list += length;
- count++;
- }
-
- return count;
-}
-
-int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
- const char *string)
-{
- int length, len, idx = 0;
- const char *list, *end;
-
- list = fdt_getprop(fdt, nodeoffset, property, &length);
- if (!list)
- return length;
-
- len = strlen(string) + 1;
- end = list + length;
-
- while (list < end) {
- length = strnlen(list, end - list) + 1;
-
- /* Abort if the last string isn't properly NUL-terminated. */
- if (list + length > end)
- return -FDT_ERR_BADVALUE;
-
- if (length == len && memcmp(list, string, length) == 0)
- return idx;
-
- list += length;
- idx++;
- }
-
- return -FDT_ERR_NOTFOUND;
-}
-
-const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
- const char *property, int idx,
- int *lenp)
-{
- const char *list, *end;
- int length;
-
- list = fdt_getprop(fdt, nodeoffset, property, &length);
- if (!list) {
- if (lenp)
- *lenp = length;
-
- return NULL;
- }
-
- end = list + length;
-
- while (list < end) {
- length = strnlen(list, end - list) + 1;
-
- /* Abort if the last string isn't properly NUL-terminated. */
- if (list + length > end) {
- if (lenp)
- *lenp = -FDT_ERR_BADVALUE;
-
- return NULL;
- }
-
- if (idx == 0) {
- if (lenp)
- *lenp = length - 1;
-
- return list;
- }
-
- list += length;
- idx--;
- }
-
- if (lenp)
- *lenp = -FDT_ERR_NOTFOUND;
-
- return NULL;
-}
-
-int fdt_node_check_compatible(const void *fdt, int nodeoffset,
- const char *compatible)
-{
- const void *prop;
- int len;
-
- prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
- if (!prop)
- return len;
-
- return !fdt_stringlist_contains(prop, len, compatible);
-}
-
-int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
- const char *compatible)
-{
- int offset, err;
-
- FDT_RO_PROBE(fdt);
-
- /* FIXME: The algorithm here is pretty horrible: we scan each
- * property of a node in fdt_node_check_compatible(), then if
- * that didn't find what we want, we scan over them again
- * making our way to the next node. Still it's the easiest to
- * implement approach; performance can come later. */
- for (offset = fdt_next_node(fdt, startoffset, NULL);
- offset >= 0;
- offset = fdt_next_node(fdt, offset, NULL)) {
- err = fdt_node_check_compatible(fdt, offset, compatible);
- if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
- return err;
- else if (err == 0)
- return offset;
- }
-
- return offset; /* error from fdt_next_node() */
-}
diff --git a/na/scripts/dtc/libfdt/fdt_rw.c b/na/scripts/dtc/libfdt/fdt_rw.c
deleted file mode 100644
index 90ea14e..0000000
--- a/na/scripts/dtc/libfdt/fdt_rw.c
+++ /dev/null
@@ -1,512 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-static int fdt_blocks_misordered_(const void *fdt,
- int mem_rsv_size, int struct_size)
-{
- return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
- || (fdt_off_dt_struct(fdt) <
- (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
- || (fdt_off_dt_strings(fdt) <
- (fdt_off_dt_struct(fdt) + struct_size))
- || (fdt_totalsize(fdt) <
- (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
-}
-
-static void fdt_downgrade_version(void *fdt)
-{
- if (!can_assume(LATEST) && fdt_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
- fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
-}
-
-static int fdt_rw_probe_(void *fdt)
-{
- if (can_assume(VALID_DTB))
- return 0;
- FDT_RO_PROBE(fdt);
-
- if (!can_assume(LATEST) && fdt_version(fdt) < 17)
- return -FDT_ERR_BADVERSION;
- if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
- fdt_size_dt_struct(fdt)))
- return -FDT_ERR_BADLAYOUT;
-
- fdt_downgrade_version(fdt);
- return 0;
-}
-
-#define FDT_RW_PROBE(fdt) \
- { \
- int err_; \
- if ((err_ = fdt_rw_probe_(fdt)) != 0) \
- return err_; \
- }
-
-static inline unsigned int fdt_data_size_(void *fdt)
-{
- return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
-}
-
-static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
-{
- char *p = splicepoint;
- unsigned int dsize = fdt_data_size_(fdt);
- size_t soff = p - (char *)fdt;
-
- if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
- return -FDT_ERR_BADOFFSET;
- if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
- return -FDT_ERR_BADOFFSET;
- if (dsize - oldlen + newlen > fdt_totalsize(fdt))
- return -FDT_ERR_NOSPACE;
- memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
- return 0;
-}
-
-static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,
- int oldn, int newn)
-{
- int delta = (newn - oldn) * sizeof(*p);
- int err;
- err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
- if (err)
- return err;
- fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
- fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
- return 0;
-}
-
-static int fdt_splice_struct_(void *fdt, void *p,
- int oldlen, int newlen)
-{
- int delta = newlen - oldlen;
- int err;
-
- if ((err = fdt_splice_(fdt, p, oldlen, newlen)))
- return err;
-
- fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
- fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
- return 0;
-}
-
-/* Must only be used to roll back in case of error */
-static void fdt_del_last_string_(void *fdt, const char *s)
-{
- int newlen = strlen(s) + 1;
-
- fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
-}
-
-static int fdt_splice_string_(void *fdt, int newlen)
-{
- void *p = (char *)fdt
- + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
- int err;
-
- if ((err = fdt_splice_(fdt, p, 0, newlen)))
- return err;
-
- fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
- return 0;
-}
-
-/**
- * fdt_find_add_string_() - Find or allocate a string
- *
- * @fdt: pointer to the device tree to check/adjust
- * @s: string to find/add
- * @allocated: Set to 0 if the string was found, 1 if not found and so
- * allocated. Ignored if can_assume(NO_ROLLBACK)
- * @return offset of string in the string table (whether found or added)
- */
-static int fdt_find_add_string_(void *fdt, const char *s, int slen,
- int *allocated)
-{
- char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
- const char *p;
- char *new;
- int err;
-
- if (!can_assume(NO_ROLLBACK))
- *allocated = 0;
-
- p = fdt_find_string_len_(strtab, fdt_size_dt_strings(fdt), s, slen);
- if (p)
- /* found it */
- return (p - strtab);
-
- new = strtab + fdt_size_dt_strings(fdt);
- err = fdt_splice_string_(fdt, slen + 1);
- if (err)
- return err;
-
- if (!can_assume(NO_ROLLBACK))
- *allocated = 1;
-
- memcpy(new, s, slen);
- new[slen] = '\0';
-
- return (new - strtab);
-}
-
-int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
-{
- struct fdt_reserve_entry *re;
- int err;
-
- FDT_RW_PROBE(fdt);
-
- re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));
- err = fdt_splice_mem_rsv_(fdt, re, 0, 1);
- if (err)
- return err;
-
- re->address = cpu_to_fdt64(address);
- re->size = cpu_to_fdt64(size);
- return 0;
-}
-
-int fdt_del_mem_rsv(void *fdt, int n)
-{
- struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n);
-
- FDT_RW_PROBE(fdt);
-
- if (n >= fdt_num_mem_rsv(fdt))
- return -FDT_ERR_NOTFOUND;
-
- return fdt_splice_mem_rsv_(fdt, re, 1, 0);
-}
-
-static int fdt_resize_property_(void *fdt, int nodeoffset,
- const char *name, int namelen,
- int len, struct fdt_property **prop)
-{
- int oldlen;
- int err;
-
- *prop = fdt_get_property_namelen_w(fdt, nodeoffset, name, namelen,
- &oldlen);
- if (!*prop)
- return oldlen;
-
- if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
- FDT_TAGALIGN(len))))
- return err;
-
- (*prop)->len = cpu_to_fdt32(len);
- return 0;
-}
-
-static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
- int namelen, int len, struct fdt_property **prop)
-{
- int proplen;
- int nextoffset;
- int namestroff;
- int err;
- int allocated;
-
- if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
- return nextoffset;
-
- namestroff = fdt_find_add_string_(fdt, name, namelen, &allocated);
- if (namestroff < 0)
- return namestroff;
-
- *prop = fdt_offset_ptr_w_(fdt, nextoffset);
- proplen = sizeof(**prop) + FDT_TAGALIGN(len);
-
- err = fdt_splice_struct_(fdt, *prop, 0, proplen);
- if (err) {
- /* Delete the string if we failed to add it */
- if (!can_assume(NO_ROLLBACK) && allocated)
- fdt_del_last_string_(fdt, name);
- return err;
- }
-
- (*prop)->tag = cpu_to_fdt32(FDT_PROP);
- (*prop)->nameoff = cpu_to_fdt32(namestroff);
- (*prop)->len = cpu_to_fdt32(len);
- return 0;
-}
-
-int fdt_set_name(void *fdt, int nodeoffset, const char *name)
-{
- char *namep;
- int oldlen, newlen;
- int err;
-
- FDT_RW_PROBE(fdt);
-
- namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
- if (!namep)
- return oldlen;
-
- newlen = strlen(name);
-
- err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),
- FDT_TAGALIGN(newlen+1));
- if (err)
- return err;
-
- memcpy(namep, name, newlen+1);
- return 0;
-}
-
-int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
- int namelen, int len, void **prop_data)
-{
- struct fdt_property *prop;
- int err;
-
- FDT_RW_PROBE(fdt);
-
- err = fdt_resize_property_(fdt, nodeoffset, name, namelen, len, &prop);
- if (err == -FDT_ERR_NOTFOUND)
- err = fdt_add_property_(fdt, nodeoffset, name, namelen, len,
- &prop);
- if (err)
- return err;
-
- *prop_data = prop->data;
- return 0;
-}
-
-int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
- int namelen, const void *val, int len)
-{
- void *prop_data;
- int err;
-
- err = fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, namelen,
- len, &prop_data);
- if (err)
- return err;
-
- if (len)
- memcpy(prop_data, val, len);
- return 0;
-}
-
-int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
- const void *val, int len)
-{
- struct fdt_property *prop;
- int err, oldlen, newlen;
-
- FDT_RW_PROBE(fdt);
-
- prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
- if (prop) {
- newlen = len + oldlen;
- err = fdt_splice_struct_(fdt, prop->data,
- FDT_TAGALIGN(oldlen),
- FDT_TAGALIGN(newlen));
- if (err)
- return err;
- prop->len = cpu_to_fdt32(newlen);
- memcpy(prop->data + oldlen, val, len);
- } else {
- err = fdt_add_property_(fdt, nodeoffset, name, strlen(name),
- len, &prop);
- if (err)
- return err;
- memcpy(prop->data, val, len);
- }
- return 0;
-}
-
-int fdt_delprop(void *fdt, int nodeoffset, const char *name)
-{
- struct fdt_property *prop;
- int len, proplen;
-
- FDT_RW_PROBE(fdt);
-
- prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
- if (!prop)
- return len;
-
- proplen = sizeof(*prop) + FDT_TAGALIGN(len);
- return fdt_splice_struct_(fdt, prop, proplen, 0);
-}
-
-int fdt_add_subnode_namelen(void *fdt, int parentoffset,
- const char *name, int namelen)
-{
- struct fdt_node_header *nh;
- int offset, nextoffset;
- int nodelen;
- int err;
- uint32_t tag;
- fdt32_t *endtag;
-
- FDT_RW_PROBE(fdt);
-
- offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
- if (offset >= 0)
- return -FDT_ERR_EXISTS;
- else if (offset != -FDT_ERR_NOTFOUND)
- return offset;
-
- /* Try to place the new node after the parent's properties */
- tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
- /* the fdt_subnode_offset_namelen() should ensure this never hits */
- if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
- return -FDT_ERR_INTERNAL;
- do {
- offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
- } while ((tag == FDT_PROP) || (tag == FDT_NOP));
-
- nh = fdt_offset_ptr_w_(fdt, offset);
- nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
-
- err = fdt_splice_struct_(fdt, nh, 0, nodelen);
- if (err)
- return err;
-
- nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
- memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
- memcpy(nh->name, name, namelen);
- endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
- *endtag = cpu_to_fdt32(FDT_END_NODE);
-
- return offset;
-}
-
-int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
-{
- return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
-}
-
-int fdt_del_node(void *fdt, int nodeoffset)
-{
- int endoffset;
-
- FDT_RW_PROBE(fdt);
-
- endoffset = fdt_node_end_offset_(fdt, nodeoffset);
- if (endoffset < 0)
- return endoffset;
-
- return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),
- endoffset - nodeoffset, 0);
-}
-
-static void fdt_packblocks_(const char *old, char *new,
- int mem_rsv_size,
- int struct_size,
- int strings_size)
-{
- int mem_rsv_off, struct_off, strings_off;
-
- mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
- struct_off = mem_rsv_off + mem_rsv_size;
- strings_off = struct_off + struct_size;
-
- memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
- fdt_set_off_mem_rsvmap(new, mem_rsv_off);
-
- memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
- fdt_set_off_dt_struct(new, struct_off);
- fdt_set_size_dt_struct(new, struct_size);
-
- memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
- fdt_set_off_dt_strings(new, strings_off);
- fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
-}
-
-int fdt_open_into(const void *fdt, void *buf, int bufsize)
-{
- int err;
- int mem_rsv_size, struct_size;
- int newsize;
- const char *fdtstart = fdt;
- const char *fdtend = fdtstart + fdt_totalsize(fdt);
- char *tmp;
-
- FDT_RO_PROBE(fdt);
-
- mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
- * sizeof(struct fdt_reserve_entry);
-
- if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
- struct_size = fdt_size_dt_struct(fdt);
- } else if (fdt_version(fdt) == 16) {
- struct_size = 0;
- while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
- ;
- if (struct_size < 0)
- return struct_size;
- } else {
- return -FDT_ERR_BADVERSION;
- }
-
- if (can_assume(LIBFDT_ORDER) ||
- !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
- /* no further work necessary */
- err = fdt_move(fdt, buf, bufsize);
- if (err)
- return err;
- fdt_set_version(buf, 17);
- fdt_set_size_dt_struct(buf, struct_size);
- fdt_set_totalsize(buf, bufsize);
- return 0;
- }
-
- /* Need to reorder */
- newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
- + struct_size + fdt_size_dt_strings(fdt);
-
- if (bufsize < newsize)
- return -FDT_ERR_NOSPACE;
-
- /* First attempt to build converted tree at beginning of buffer */
- tmp = buf;
- /* But if that overlaps with the old tree... */
- if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
- /* Try right after the old tree instead */
- tmp = (char *)(uintptr_t)fdtend;
- if ((tmp + newsize) > ((char *)buf + bufsize))
- return -FDT_ERR_NOSPACE;
- }
-
- fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
- fdt_size_dt_strings(fdt));
- memmove(buf, tmp, newsize);
-
- fdt_set_magic(buf, FDT_MAGIC);
- fdt_set_totalsize(buf, bufsize);
- fdt_set_version(buf, 17);
- fdt_set_last_comp_version(buf, 16);
- fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
-
- return 0;
-}
-
-int fdt_pack(void *fdt)
-{
- int mem_rsv_size;
-
- FDT_RW_PROBE(fdt);
-
- mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
- * sizeof(struct fdt_reserve_entry);
- fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
- fdt_size_dt_strings(fdt));
- fdt_set_totalsize(fdt, fdt_data_size_(fdt));
-
- return 0;
-}
diff --git a/na/scripts/dtc/libfdt/fdt_strerror.c b/na/scripts/dtc/libfdt/fdt_strerror.c
deleted file mode 100644
index d852b77..0000000
--- a/na/scripts/dtc/libfdt/fdt_strerror.c
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-struct fdt_errtabent {
- const char *str;
-};
-
-#define FDT_ERRTABENT(val) \
- [(val)] = { .str = #val, }
-
-static struct fdt_errtabent fdt_errtable[] = {
- FDT_ERRTABENT(FDT_ERR_NOTFOUND),
- FDT_ERRTABENT(FDT_ERR_EXISTS),
- FDT_ERRTABENT(FDT_ERR_NOSPACE),
-
- FDT_ERRTABENT(FDT_ERR_BADOFFSET),
- FDT_ERRTABENT(FDT_ERR_BADPATH),
- FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
- FDT_ERRTABENT(FDT_ERR_BADSTATE),
-
- FDT_ERRTABENT(FDT_ERR_TRUNCATED),
- FDT_ERRTABENT(FDT_ERR_BADMAGIC),
- FDT_ERRTABENT(FDT_ERR_BADVERSION),
- FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
- FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
- FDT_ERRTABENT(FDT_ERR_INTERNAL),
- FDT_ERRTABENT(FDT_ERR_BADNCELLS),
- FDT_ERRTABENT(FDT_ERR_BADVALUE),
- FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
- FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
- FDT_ERRTABENT(FDT_ERR_BADFLAGS),
- FDT_ERRTABENT(FDT_ERR_ALIGNMENT),
-};
-#define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0])))
-
-const char *fdt_strerror(int errval)
-{
- if (errval > 0)
- return "<valid offset/length>";
- else if (errval == 0)
- return "<no error>";
- else if (-errval < FDT_ERRTABSIZE) {
- const char *s = fdt_errtable[-errval].str;
-
- if (s)
- return s;
- }
-
- return "<unknown error>";
-}
diff --git a/na/scripts/dtc/libfdt/fdt_sw.c b/na/scripts/dtc/libfdt/fdt_sw.c
deleted file mode 100644
index 4c569ee..0000000
--- a/na/scripts/dtc/libfdt/fdt_sw.c
+++ /dev/null
@@ -1,384 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-static int fdt_sw_probe_(void *fdt)
-{
- if (!can_assume(VALID_INPUT)) {
- if (fdt_magic(fdt) == FDT_MAGIC)
- return -FDT_ERR_BADSTATE;
- else if (fdt_magic(fdt) != FDT_SW_MAGIC)
- return -FDT_ERR_BADMAGIC;
- }
-
- return 0;
-}
-
-#define FDT_SW_PROBE(fdt) \
- { \
- int err; \
- if ((err = fdt_sw_probe_(fdt)) != 0) \
- return err; \
- }
-
-/* 'memrsv' state: Initial state after fdt_create()
- *
- * Allowed functions:
- * fdt_add_reservemap_entry()
- * fdt_finish_reservemap() [moves to 'struct' state]
- */
-static int fdt_sw_probe_memrsv_(void *fdt)
-{
- int err = fdt_sw_probe_(fdt);
- if (err)
- return err;
-
- if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0)
- return -FDT_ERR_BADSTATE;
- return 0;
-}
-
-#define FDT_SW_PROBE_MEMRSV(fdt) \
- { \
- int err; \
- if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
- return err; \
- }
-
-/* 'struct' state: Enter this state after fdt_finish_reservemap()
- *
- * Allowed functions:
- * fdt_begin_node()
- * fdt_end_node()
- * fdt_property*()
- * fdt_finish() [moves to 'complete' state]
- */
-static int fdt_sw_probe_struct_(void *fdt)
-{
- int err = fdt_sw_probe_(fdt);
- if (err)
- return err;
-
- if (!can_assume(VALID_INPUT) &&
- fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
- return -FDT_ERR_BADSTATE;
- return 0;
-}
-
-#define FDT_SW_PROBE_STRUCT(fdt) \
- { \
- int err; \
- if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
- return err; \
- }
-
-static inline uint32_t sw_flags(void *fdt)
-{
- /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
- return fdt_last_comp_version(fdt);
-}
-
-/* 'complete' state: Enter this state after fdt_finish()
- *
- * Allowed functions: none
- */
-
-static void *fdt_grab_space_(void *fdt, size_t len)
-{
- unsigned int offset = fdt_size_dt_struct(fdt);
- unsigned int spaceleft;
-
- spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
- - fdt_size_dt_strings(fdt);
-
- if ((offset + len < offset) || (offset + len > spaceleft))
- return NULL;
-
- fdt_set_size_dt_struct(fdt, offset + len);
- return fdt_offset_ptr_w_(fdt, offset);
-}
-
-int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
-{
- const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
- sizeof(struct fdt_reserve_entry));
- void *fdt = buf;
-
- if (bufsize < hdrsize)
- return -FDT_ERR_NOSPACE;
-
- if (flags & ~FDT_CREATE_FLAGS_ALL)
- return -FDT_ERR_BADFLAGS;
-
- memset(buf, 0, bufsize);
-
- /*
- * magic and last_comp_version keep intermediate state during the fdt
- * creation process, which is replaced with the proper FDT format by
- * fdt_finish().
- *
- * flags should be accessed with sw_flags().
- */
- fdt_set_magic(fdt, FDT_SW_MAGIC);
- fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
- fdt_set_last_comp_version(fdt, flags);
-
- fdt_set_totalsize(fdt, bufsize);
-
- fdt_set_off_mem_rsvmap(fdt, hdrsize);
- fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
- fdt_set_off_dt_strings(fdt, 0);
-
- return 0;
-}
-
-int fdt_create(void *buf, int bufsize)
-{
- return fdt_create_with_flags(buf, bufsize, 0);
-}
-
-int fdt_resize(void *fdt, void *buf, int bufsize)
-{
- size_t headsize, tailsize;
- char *oldtail, *newtail;
-
- FDT_SW_PROBE(fdt);
-
- if (bufsize < 0)
- return -FDT_ERR_NOSPACE;
-
- headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
- tailsize = fdt_size_dt_strings(fdt);
-
- if (!can_assume(VALID_DTB) &&
- headsize + tailsize > fdt_totalsize(fdt))
- return -FDT_ERR_INTERNAL;
-
- if ((headsize + tailsize) > (unsigned)bufsize)
- return -FDT_ERR_NOSPACE;
-
- oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
- newtail = (char *)buf + bufsize - tailsize;
-
- /* Two cases to avoid clobbering data if the old and new
- * buffers partially overlap */
- if (buf <= fdt) {
- memmove(buf, fdt, headsize);
- memmove(newtail, oldtail, tailsize);
- } else {
- memmove(newtail, oldtail, tailsize);
- memmove(buf, fdt, headsize);
- }
-
- fdt_set_totalsize(buf, bufsize);
- if (fdt_off_dt_strings(buf))
- fdt_set_off_dt_strings(buf, bufsize);
-
- return 0;
-}
-
-int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
-{
- struct fdt_reserve_entry *re;
- int offset;
-
- FDT_SW_PROBE_MEMRSV(fdt);
-
- offset = fdt_off_dt_struct(fdt);
- if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
- return -FDT_ERR_NOSPACE;
-
- re = (struct fdt_reserve_entry *)((char *)fdt + offset);
- re->address = cpu_to_fdt64(addr);
- re->size = cpu_to_fdt64(size);
-
- fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
-
- return 0;
-}
-
-int fdt_finish_reservemap(void *fdt)
-{
- int err = fdt_add_reservemap_entry(fdt, 0, 0);
-
- if (err)
- return err;
-
- fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
- return 0;
-}
-
-int fdt_begin_node(void *fdt, const char *name)
-{
- struct fdt_node_header *nh;
- int namelen;
-
- FDT_SW_PROBE_STRUCT(fdt);
-
- namelen = strlen(name) + 1;
- nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
- if (! nh)
- return -FDT_ERR_NOSPACE;
-
- nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
- memcpy(nh->name, name, namelen);
- return 0;
-}
-
-int fdt_end_node(void *fdt)
-{
- fdt32_t *en;
-
- FDT_SW_PROBE_STRUCT(fdt);
-
- en = fdt_grab_space_(fdt, FDT_TAGSIZE);
- if (! en)
- return -FDT_ERR_NOSPACE;
-
- *en = cpu_to_fdt32(FDT_END_NODE);
- return 0;
-}
-
-static int fdt_add_string_(void *fdt, const char *s)
-{
- char *strtab = (char *)fdt + fdt_totalsize(fdt);
- unsigned int strtabsize = fdt_size_dt_strings(fdt);
- unsigned int len = strlen(s) + 1;
- unsigned int struct_top, offset;
-
- offset = strtabsize + len;
- struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
- if (fdt_totalsize(fdt) - offset < struct_top)
- return 0; /* no more room :( */
-
- memcpy(strtab - offset, s, len);
- fdt_set_size_dt_strings(fdt, strtabsize + len);
- return -offset;
-}
-
-/* Must only be used to roll back in case of error */
-static void fdt_del_last_string_(void *fdt, const char *s)
-{
- int strtabsize = fdt_size_dt_strings(fdt);
- int len = strlen(s) + 1;
-
- fdt_set_size_dt_strings(fdt, strtabsize - len);
-}
-
-static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
-{
- char *strtab = (char *)fdt + fdt_totalsize(fdt);
- int strtabsize = fdt_size_dt_strings(fdt);
- const char *p;
-
- *allocated = 0;
-
- p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
- if (p)
- return p - strtab;
-
- *allocated = 1;
-
- return fdt_add_string_(fdt, s);
-}
-
-int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
-{
- struct fdt_property *prop;
- int nameoff;
- int allocated;
-
- FDT_SW_PROBE_STRUCT(fdt);
-
- /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
- if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
- allocated = 1;
- nameoff = fdt_add_string_(fdt, name);
- } else {
- nameoff = fdt_find_add_string_(fdt, name, &allocated);
- }
- if (nameoff == 0)
- return -FDT_ERR_NOSPACE;
-
- prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
- if (! prop) {
- if (allocated)
- fdt_del_last_string_(fdt, name);
- return -FDT_ERR_NOSPACE;
- }
-
- prop->tag = cpu_to_fdt32(FDT_PROP);
- prop->nameoff = cpu_to_fdt32(nameoff);
- prop->len = cpu_to_fdt32(len);
- *valp = prop->data;
- return 0;
-}
-
-int fdt_property(void *fdt, const char *name, const void *val, int len)
-{
- void *ptr;
- int ret;
-
- ret = fdt_property_placeholder(fdt, name, len, &ptr);
- if (ret)
- return ret;
- memcpy(ptr, val, len);
- return 0;
-}
-
-int fdt_finish(void *fdt)
-{
- char *p = (char *)fdt;
- fdt32_t *end;
- int oldstroffset, newstroffset;
- uint32_t tag;
- int offset, nextoffset;
-
- FDT_SW_PROBE_STRUCT(fdt);
-
- /* Add terminator */
- end = fdt_grab_space_(fdt, sizeof(*end));
- if (! end)
- return -FDT_ERR_NOSPACE;
- *end = cpu_to_fdt32(FDT_END);
-
- /* Relocate the string table */
- oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
- newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
- memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
- fdt_set_off_dt_strings(fdt, newstroffset);
-
- /* Walk the structure, correcting string offsets */
- offset = 0;
- while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
- if (tag == FDT_PROP) {
- struct fdt_property *prop =
- fdt_offset_ptr_w_(fdt, offset);
- int nameoff;
-
- nameoff = fdt32_to_cpu(prop->nameoff);
- nameoff += fdt_size_dt_strings(fdt);
- prop->nameoff = cpu_to_fdt32(nameoff);
- }
- offset = nextoffset;
- }
- if (nextoffset < 0)
- return nextoffset;
-
- /* Finally, adjust the header */
- fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
-
- /* And fix up fields that were keeping intermediate state. */
- fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
- fdt_set_magic(fdt, FDT_MAGIC);
-
- return 0;
-}
diff --git a/na/scripts/dtc/libfdt/fdt_wip.c b/na/scripts/dtc/libfdt/fdt_wip.c
deleted file mode 100644
index c2d7566..0000000
--- a/na/scripts/dtc/libfdt/fdt_wip.c
+++ /dev/null
@@ -1,94 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- */
-#include "libfdt_env.h"
-
-#include <fdt.h>
-#include <libfdt.h>
-
-#include "libfdt_internal.h"
-
-int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
- const char *name, int namelen,
- uint32_t idx, const void *val,
- int len)
-{
- void *propval;
- int proplen;
-
- propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
- &proplen);
- if (!propval)
- return proplen;
-
- if ((unsigned)proplen < (len + idx))
- return -FDT_ERR_NOSPACE;
-
- memcpy((char *)propval + idx, val, len);
- return 0;
-}
-
-int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
- const void *val, int len)
-{
- const void *propval;
- int proplen;
-
- propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
- if (!propval)
- return proplen;
-
- if (proplen != len)
- return -FDT_ERR_NOSPACE;
-
- return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
- strlen(name), 0,
- val, len);
-}
-
-static void fdt_nop_region_(void *start, int len)
-{
- fdt32_t *p;
-
- for (p = start; (char *)p < ((char *)start + len); p++)
- *p = cpu_to_fdt32(FDT_NOP);
-}
-
-int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
-{
- struct fdt_property *prop;
- int len;
-
- prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
- if (!prop)
- return len;
-
- fdt_nop_region_(prop, len + sizeof(*prop));
-
- return 0;
-}
-
-int fdt_node_end_offset_(void *fdt, int offset)
-{
- int depth = 0;
-
- while ((offset >= 0) && (depth >= 0))
- offset = fdt_next_node(fdt, offset, &depth);
-
- return offset;
-}
-
-int fdt_nop_node(void *fdt, int nodeoffset)
-{
- int endoffset;
-
- endoffset = fdt_node_end_offset_(fdt, nodeoffset);
- if (endoffset < 0)
- return endoffset;
-
- fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0),
- endoffset - nodeoffset);
- return 0;
-}
diff --git a/na/scripts/dtc/libfdt/libfdt.h b/na/scripts/dtc/libfdt/libfdt.h
deleted file mode 100644
index 81aaf7c..0000000
--- a/na/scripts/dtc/libfdt/libfdt.h
+++ /dev/null
@@ -1,2540 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
-#ifndef LIBFDT_H
-#define LIBFDT_H
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- */
-
-#include "libfdt_env.h"
-#include "fdt.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define FDT_FIRST_SUPPORTED_VERSION 0x02
-#define FDT_LAST_COMPATIBLE_VERSION 0x10
-#define FDT_LAST_SUPPORTED_VERSION 0x11
-
-/* Error codes: informative error codes */
-#define FDT_ERR_NOTFOUND 1
- /* FDT_ERR_NOTFOUND: The requested node or property does not exist */
-#define FDT_ERR_EXISTS 2
- /* FDT_ERR_EXISTS: Attempted to create a node or property which
- * already exists */
-#define FDT_ERR_NOSPACE 3
- /* FDT_ERR_NOSPACE: Operation needed to expand the device
- * tree, but its buffer did not have sufficient space to
- * contain the expanded tree. Use fdt_open_into() to move the
- * device tree to a buffer with more space. */
-
-/* Error codes: codes for bad parameters */
-#define FDT_ERR_BADOFFSET 4
- /* FDT_ERR_BADOFFSET: Function was passed a structure block
- * offset which is out-of-bounds, or which points to an
- * unsuitable part of the structure for the operation. */
-#define FDT_ERR_BADPATH 5
- /* FDT_ERR_BADPATH: Function was passed a badly formatted path
- * (e.g. missing a leading / for a function which requires an
- * absolute path) */
-#define FDT_ERR_BADPHANDLE 6
- /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle.
- * This can be caused either by an invalid phandle property
- * length, or the phandle value was either 0 or -1, which are
- * not permitted. */
-#define FDT_ERR_BADSTATE 7
- /* FDT_ERR_BADSTATE: Function was passed an incomplete device
- * tree created by the sequential-write functions, which is
- * not sufficiently complete for the requested operation. */
-
-/* Error codes: codes for bad device tree blobs */
-#define FDT_ERR_TRUNCATED 8
- /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly
- * terminated (overflows, goes outside allowed bounds, or
- * isn't properly terminated). */
-#define FDT_ERR_BADMAGIC 9
- /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a
- * device tree at all - it is missing the flattened device
- * tree magic number. */
-#define FDT_ERR_BADVERSION 10
- /* FDT_ERR_BADVERSION: Given device tree has a version which
- * can't be handled by the requested operation. For
- * read-write functions, this may mean that fdt_open_into() is
- * required to convert the tree to the expected version. */
-#define FDT_ERR_BADSTRUCTURE 11
- /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt
- * structure block or other serious error (e.g. misnested
- * nodes, or subnodes preceding properties). */
-#define FDT_ERR_BADLAYOUT 12
- /* FDT_ERR_BADLAYOUT: For read-write functions, the given
- * device tree has it's sub-blocks in an order that the
- * function can't handle (memory reserve map, then structure,
- * then strings). Use fdt_open_into() to reorganize the tree
- * into a form suitable for the read-write operations. */
-
-/* "Can't happen" error indicating a bug in libfdt */
-#define FDT_ERR_INTERNAL 13
- /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion.
- * Should never be returned, if it is, it indicates a bug in
- * libfdt itself. */
-
-/* Errors in device tree content */
-#define FDT_ERR_BADNCELLS 14
- /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells
- * or similar property with a bad format or value */
-
-#define FDT_ERR_BADVALUE 15
- /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected
- * value. For example: a property expected to contain a string list
- * is not NUL-terminated within the length of its value. */
-
-#define FDT_ERR_BADOVERLAY 16
- /* FDT_ERR_BADOVERLAY: The device tree overlay, while
- * correctly structured, cannot be applied due to some
- * unexpected or missing value, property or node. */
-
-#define FDT_ERR_NOPHANDLES 17
- /* FDT_ERR_NOPHANDLES: The device tree doesn't have any
- * phandle available anymore without causing an overflow */
-
-#define FDT_ERR_BADFLAGS 18
- /* FDT_ERR_BADFLAGS: The function was passed a flags field that
- * contains invalid flags or an invalid combination of flags. */
-
-#define FDT_ERR_ALIGNMENT 19
- /* FDT_ERR_ALIGNMENT: The device tree base address is not 8-byte
- * aligned. */
-
-#define FDT_ERR_MAX 19
-
-/* constants */
-#define FDT_MAX_PHANDLE 0xfffffffe
- /* Valid values for phandles range from 1 to 2^32-2. */
-
-/**********************************************************************/
-/* Low-level functions (you probably don't need these) */
-/**********************************************************************/
-
-/**
- * fdt_offset_ptr - safely get a byte range within the device tree blob
- * @fdt: Pointer to the device tree blob
- * @offset: Offset within the blob to the desired byte range
- * @checklen: Required length of the byte range
- *
- * fdt_offset_ptr() returns a pointer to the byte range of length @checklen at
- * the given @offset within the device tree blob, after verifying that the byte
- * range fits entirely within the blob and does not overflow.
- *
- * returns:
- * pointer to the byte range, on success
- * NULL, if the requested range does not fit within the blob
- */
-#ifndef SWIG /* This function is not useful in Python */
-const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen);
-#endif
-static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
-{
- return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen);
-}
-
-/**
- * fdt_next_tag - get next tag in the device tree
- * @fdt: Pointer to the device tree blob
- * @offset: Offset within the blob to start searching
- * @nextoffset: Pointer to variable to store the offset of the next tag
- *
- * fdt_next_tag() returns the tag type of the next tag in the device tree
- * blob starting from the given @offset. If @nextoffset is non-NULL, it will
- * be set to the offset immediately following the tag.
- *
- * returns:
- * the tag type (FDT_BEGIN_NODE, FDT_END_NODE, FDT_PROP, FDT_NOP, FDT_END),
- * FDT_END, if offset is out of bounds
- */
-uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
-
-/*
- * External helpers to access words from a device tree blob. They're built
- * to work even with unaligned pointers on platforms (such as ARMv5) that don't
- * like unaligned loads and stores.
- */
-static inline uint16_t fdt16_ld(const fdt16_t *p)
-{
- const uint8_t *bp = (const uint8_t *)p;
-
- return ((uint16_t)bp[0] << 8) | bp[1];
-}
-
-static inline uint32_t fdt32_ld(const fdt32_t *p)
-{
- const uint8_t *bp = (const uint8_t *)p;
-
- return ((uint32_t)bp[0] << 24)
- | ((uint32_t)bp[1] << 16)
- | ((uint32_t)bp[2] << 8)
- | bp[3];
-}
-
-static inline void fdt32_st(void *property, uint32_t value)
-{
- uint8_t *bp = (uint8_t *)property;
-
- bp[0] = value >> 24;
- bp[1] = (value >> 16) & 0xff;
- bp[2] = (value >> 8) & 0xff;
- bp[3] = value & 0xff;
-}
-
-static inline uint64_t fdt64_ld(const fdt64_t *p)
-{
- const uint8_t *bp = (const uint8_t *)p;
-
- return ((uint64_t)bp[0] << 56)
- | ((uint64_t)bp[1] << 48)
- | ((uint64_t)bp[2] << 40)
- | ((uint64_t)bp[3] << 32)
- | ((uint64_t)bp[4] << 24)
- | ((uint64_t)bp[5] << 16)
- | ((uint64_t)bp[6] << 8)
- | bp[7];
-}
-
-static inline void fdt64_st(void *property, uint64_t value)
-{
- uint8_t *bp = (uint8_t *)property;
-
- bp[0] = value >> 56;
- bp[1] = (value >> 48) & 0xff;
- bp[2] = (value >> 40) & 0xff;
- bp[3] = (value >> 32) & 0xff;
- bp[4] = (value >> 24) & 0xff;
- bp[5] = (value >> 16) & 0xff;
- bp[6] = (value >> 8) & 0xff;
- bp[7] = value & 0xff;
-}
-
-/**********************************************************************/
-/* Traversal functions */
-/**********************************************************************/
-
-int fdt_next_node(const void *fdt, int offset, int *depth);
-
-/**
- * fdt_first_subnode() - get offset of first direct subnode
- * @fdt: FDT blob
- * @offset: Offset of node to check
- *
- * Return: offset of first subnode, or -FDT_ERR_NOTFOUND if there is none
- */
-int fdt_first_subnode(const void *fdt, int offset);
-
-/**
- * fdt_next_subnode() - get offset of next direct subnode
- * @fdt: FDT blob
- * @offset: Offset of previous subnode
- *
- * After first calling fdt_first_subnode(), call this function repeatedly to
- * get direct subnodes of a parent node.
- *
- * Return: offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more
- * subnodes
- */
-int fdt_next_subnode(const void *fdt, int offset);
-
-/**
- * fdt_for_each_subnode - iterate over all subnodes of a parent
- *
- * @node: child node (int, lvalue)
- * @fdt: FDT blob (const void *)
- * @parent: parent node (int)
- *
- * This is actually a wrapper around a for loop and would be used like so:
- *
- * fdt_for_each_subnode(node, fdt, parent) {
- * Use node
- * ...
- * }
- *
- * if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) {
- * Error handling
- * }
- *
- * Note that this is implemented as a macro and @node is used as
- * iterator in the loop. The parent variable be constant or even a
- * literal.
- */
-#define fdt_for_each_subnode(node, fdt, parent) \
- for (node = fdt_first_subnode(fdt, parent); \
- node >= 0; \
- node = fdt_next_subnode(fdt, node))
-
-/**********************************************************************/
-/* General functions */
-/**********************************************************************/
-#define fdt_get_header(fdt, field) \
- (fdt32_ld(&((const struct fdt_header *)(fdt))->field))
-#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
-#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
-#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
-#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
-#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
-#define fdt_version(fdt) (fdt_get_header(fdt, version))
-#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
-#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
-#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
-#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
-
-#define fdt_set_hdr_(name) \
- static inline void fdt_set_##name(void *fdt, uint32_t val) \
- { \
- struct fdt_header *fdth = (struct fdt_header *)fdt; \
- fdth->name = cpu_to_fdt32(val); \
- }
-fdt_set_hdr_(magic)
-fdt_set_hdr_(totalsize)
-fdt_set_hdr_(off_dt_struct)
-fdt_set_hdr_(off_dt_strings)
-fdt_set_hdr_(off_mem_rsvmap)
-fdt_set_hdr_(version)
-fdt_set_hdr_(last_comp_version)
-fdt_set_hdr_(boot_cpuid_phys)
-fdt_set_hdr_(size_dt_strings)
-fdt_set_hdr_(size_dt_struct)
-#undef fdt_set_hdr_
-
-/**
- * fdt_header_size - return the size of the tree's header
- * @fdt: pointer to a flattened device tree
- *
- * Return: size of DTB header in bytes
- */
-size_t fdt_header_size(const void *fdt);
-
-/**
- * fdt_header_size_ - internal function to get header size from a version number
- * @version: device tree version number
- *
- * Return: size of DTB header in bytes
- */
-size_t fdt_header_size_(uint32_t version);
-
-/**
- * fdt_check_header - sanity check a device tree header
- * @fdt: pointer to data which might be a flattened device tree
- *
- * fdt_check_header() checks that the given buffer contains what
- * appears to be a flattened device tree, and that the header contains
- * valid information (to the extent that can be determined from the
- * header alone).
- *
- * returns:
- * 0, if the buffer appears to contain a valid device tree
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_TRUNCATED, standard meanings, as above
- */
-int fdt_check_header(const void *fdt);
-
-/**
- * fdt_move - move a device tree around in memory
- * @fdt: pointer to the device tree to move
- * @buf: pointer to memory where the device is to be moved
- * @bufsize: size of the memory space at buf
- *
- * fdt_move() relocates, if possible, the device tree blob located at
- * fdt to the buffer at buf of size bufsize. The buffer may overlap
- * with the existing device tree blob at fdt. Therefore,
- * fdt_move(fdt, fdt, fdt_totalsize(fdt))
- * should always succeed.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE, standard meanings
- */
-int fdt_move(const void *fdt, void *buf, int bufsize);
-
-/**********************************************************************/
-/* Read-only functions */
-/**********************************************************************/
-
-/**
- * fdt_check_full - check device tree validity
- * @fdt: pointer to the device tree blob
- * @bufsize: size of the buffer containing the device tree
- *
- * fdt_check_full() checks that the given buffer contains a valid
- * flattened device tree and that the tree structure is internally
- * consistent. This is a more thorough check than fdt_check_header().
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_check_full(const void *fdt, size_t bufsize);
-
-/**
- * fdt_get_string - retrieve a string from the strings block of a device tree
- * @fdt: pointer to the device tree blob
- * @stroffset: offset of the string within the strings block (native endian)
- * @lenp: optional pointer to return the string's length
- *
- * fdt_get_string() retrieves a pointer to a single string from the
- * strings block of the device tree blob at fdt, and optionally also
- * returns the string's length in *lenp.
- *
- * returns:
- * a pointer to the string, on success
- * NULL, if stroffset is out of bounds, or doesn't point to a valid string
- */
-const char *fdt_get_string(const void *fdt, int stroffset, int *lenp);
-
-/**
- * fdt_string - retrieve a string from the strings block of a device tree
- * @fdt: pointer to the device tree blob
- * @stroffset: offset of the string within the strings block (native endian)
- *
- * fdt_string() retrieves a pointer to a single string from the
- * strings block of the device tree blob at fdt.
- *
- * returns:
- * a pointer to the string, on success
- * NULL, if stroffset is out of bounds, or doesn't point to a valid string
- */
-const char *fdt_string(const void *fdt, int stroffset);
-
-/**
- * fdt_find_max_phandle - find and return the highest phandle in a tree
- * @fdt: pointer to the device tree blob
- * @phandle: return location for the highest phandle value found in the tree
- *
- * fdt_find_max_phandle() finds the highest phandle value in the given device
- * tree. The value returned in @phandle is only valid if the function returns
- * success.
- *
- * returns:
- * 0 on success or a negative error code on failure
- */
-int fdt_find_max_phandle(const void *fdt, uint32_t *phandle);
-
-/**
- * fdt_get_max_phandle - retrieves the highest phandle in a tree
- * @fdt: pointer to the device tree blob
- *
- * fdt_get_max_phandle retrieves the highest phandle in the given
- * device tree. This will ignore badly formatted phandles, or phandles
- * with a value of 0 or -1.
- *
- * This function is deprecated in favour of fdt_find_max_phandle().
- *
- * returns:
- * the highest phandle on success
- * 0, if no phandle was found in the device tree
- * -1, if an error occurred
- */
-static inline uint32_t fdt_get_max_phandle(const void *fdt)
-{
- uint32_t phandle;
- int err;
-
- err = fdt_find_max_phandle(fdt, &phandle);
- if (err < 0)
- return (uint32_t)-1;
-
- return phandle;
-}
-
-/**
- * fdt_generate_phandle - return a new, unused phandle for a device tree blob
- * @fdt: pointer to the device tree blob
- * @phandle: return location for the new phandle
- *
- * Walks the device tree blob and looks for the highest phandle value. On
- * success, the new, unused phandle value (one higher than the previously
- * highest phandle value in the device tree blob) will be returned in the
- * @phandle parameter.
- *
- * Return: 0 on success or a negative error-code on failure
- */
-int fdt_generate_phandle(const void *fdt, uint32_t *phandle);
-
-/**
- * fdt_num_mem_rsv - retrieve the number of memory reserve map entries
- * @fdt: pointer to the device tree blob
- *
- * Returns the number of entries in the device tree blob's memory
- * reservation map. This does not include the terminating 0,0 entry
- * or any other (0,0) entries reserved for expansion.
- *
- * returns:
- * the number of entries
- */
-int fdt_num_mem_rsv(const void *fdt);
-
-/**
- * fdt_get_mem_rsv - retrieve one memory reserve map entry
- * @fdt: pointer to the device tree blob
- * @n: index of reserve map entry
- * @address: pointer to 64-bit variable to hold the start address
- * @size: pointer to 64-bit variable to hold the size of the entry
- *
- * On success, @address and @size will contain the address and size of
- * the n-th reserve map entry from the device tree blob, in
- * native-endian format.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE, standard meanings
- */
-int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
-
-/**
- * fdt_subnode_offset_namelen - find a subnode based on substring
- * @fdt: pointer to the device tree blob
- * @parentoffset: structure block offset of a node
- * @name: name of the subnode to locate
- * @namelen: number of characters of name to consider
- *
- * Identical to fdt_subnode_offset(), but only examine the first
- * namelen characters of name for matching the subnode name. This is
- * useful for finding subnodes based on a portion of a larger string,
- * such as a full path.
- *
- * Return: offset of the subnode or -FDT_ERR_NOTFOUND if name not found.
- */
-#ifndef SWIG /* Not available in Python */
-int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
- const char *name, int namelen);
-#endif
-/**
- * fdt_subnode_offset - find a subnode of a given node
- * @fdt: pointer to the device tree blob
- * @parentoffset: structure block offset of a node
- * @name: name of the subnode to locate
- *
- * fdt_subnode_offset() finds a subnode of the node at structure block
- * offset parentoffset with the given name. name may include a unit
- * address, in which case fdt_subnode_offset() will find the subnode
- * with that unit address, or the unit address may be omitted, in
- * which case fdt_subnode_offset() will find an arbitrary subnode
- * whose name excluding unit address matches the given name.
- *
- * returns:
- * structure block offset of the requested subnode (>=0), on success
- * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
- * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
- * tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings.
- */
-int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
-
-/**
- * fdt_path_offset_namelen - find a tree node by its full path
- * @fdt: pointer to the device tree blob
- * @path: full path of the node to locate
- * @namelen: number of characters of path to consider
- *
- * Identical to fdt_path_offset(), but only consider the first namelen
- * characters of path as the path name.
- *
- * Return: offset of the node or negative libfdt error value otherwise
- */
-#ifndef SWIG /* Not available in Python */
-int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
-#endif
-
-/**
- * fdt_path_offset - find a tree node by its full path
- * @fdt: pointer to the device tree blob
- * @path: full path of the node to locate
- *
- * fdt_path_offset() finds a node of a given path in the device tree.
- * Each path component may omit the unit address portion, but the
- * results of this are undefined if any such path component is
- * ambiguous (that is if there are multiple nodes at the relevant
- * level matching the given component, differentiated only by unit
- * address).
- *
- * If the path is not absolute (i.e. does not begin with '/'), the
- * first component is treated as an alias. That is, the property by
- * that name is looked up in the /aliases node, and the value of that
- * property used in place of that first component.
- *
- * For example, for this small fragment
- *
- * / {
- * aliases {
- * i2c2 = &foo; // RHS compiles to "/soc@0/i2c@30a40000/eeprom@52"
- * };
- * soc@0 {
- * foo: i2c@30a40000 {
- * bar: eeprom@52 {
- * };
- * };
- * };
- * };
- *
- * these would be equivalent:
- *
- * /soc@0/i2c@30a40000/eeprom@52
- * i2c2/eeprom@52
- *
- * returns:
- * structure block offset of the node with the requested path (>=0), on
- * success
- * -FDT_ERR_BADPATH, given path does not begin with '/' and the first
- * component is not a valid alias
- * -FDT_ERR_NOTFOUND, if the requested node does not exist
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings.
- */
-int fdt_path_offset(const void *fdt, const char *path);
-
-/**
- * fdt_get_name - retrieve the name of a given node
- * @fdt: pointer to the device tree blob
- * @nodeoffset: structure block offset of the starting node
- * @lenp: pointer to an integer variable (will be overwritten) or NULL
- *
- * fdt_get_name() retrieves the name (including unit address) of the
- * device tree node at structure block offset nodeoffset. If lenp is
- * non-NULL, the length of this name is also returned, in the integer
- * pointed to by lenp.
- *
- * returns:
- * pointer to the node's name, on success
- * If lenp is non-NULL, *lenp contains the length of that name
- * (>=0)
- * NULL, on error
- * if lenp is non-NULL *lenp contains an error code (<0):
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
- * tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE, standard meanings
- */
-const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
-
-/**
- * fdt_first_property_offset - find the offset of a node's first property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: structure block offset of a node
- *
- * fdt_first_property_offset() finds the first property of the node at
- * the given structure block offset.
- *
- * returns:
- * structure block offset of the property (>=0), on success
- * -FDT_ERR_NOTFOUND, if the requested node has no properties
- * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings.
- */
-int fdt_first_property_offset(const void *fdt, int nodeoffset);
-
-/**
- * fdt_next_property_offset - step through a node's properties
- * @fdt: pointer to the device tree blob
- * @offset: structure block offset of a property
- *
- * fdt_next_property_offset() finds the property immediately after the
- * one at the given structure block offset. This will be a property
- * of the same node as the given property.
- *
- * returns:
- * structure block offset of the next property (>=0), on success
- * -FDT_ERR_NOTFOUND, if the given property is the last in its node
- * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings.
- */
-int fdt_next_property_offset(const void *fdt, int offset);
-
-/**
- * fdt_for_each_property_offset - iterate over all properties of a node
- *
- * @property: property offset (int, lvalue)
- * @fdt: FDT blob (const void *)
- * @node: node offset (int)
- *
- * This is actually a wrapper around a for loop and would be used like so:
- *
- * fdt_for_each_property_offset(property, fdt, node) {
- * Use property
- * ...
- * }
- *
- * if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) {
- * Error handling
- * }
- *
- * Note that this is implemented as a macro and property is used as
- * iterator in the loop. The node variable can be constant or even a
- * literal.
- */
-#define fdt_for_each_property_offset(property, fdt, node) \
- for (property = fdt_first_property_offset(fdt, node); \
- property >= 0; \
- property = fdt_next_property_offset(fdt, property))
-
-/**
- * fdt_get_property_by_offset - retrieve the property at a given offset
- * @fdt: pointer to the device tree blob
- * @offset: offset of the property to retrieve
- * @lenp: pointer to an integer variable (will be overwritten) or NULL
- *
- * fdt_get_property_by_offset() retrieves a pointer to the
- * fdt_property structure within the device tree blob at the given
- * offset. If lenp is non-NULL, the length of the property value is
- * also returned, in the integer pointed to by lenp.
- *
- * Note that this code only works on device tree versions >= 16. fdt_getprop()
- * works on all versions.
- *
- * returns:
- * pointer to the structure representing the property
- * if lenp is non-NULL, *lenp contains the length of the property
- * value (>=0)
- * NULL, on error
- * if lenp is non-NULL, *lenp contains an error code (<0):
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
- int offset,
- int *lenp);
-static inline struct fdt_property *fdt_get_property_by_offset_w(void *fdt,
- int offset,
- int *lenp)
-{
- return (struct fdt_property *)(uintptr_t)
- fdt_get_property_by_offset(fdt, offset, lenp);
-}
-
-/**
- * fdt_get_property_namelen - find a property based on substring
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to find
- * @name: name of the property to find
- * @namelen: number of characters of name to consider
- * @lenp: pointer to an integer variable (will be overwritten) or NULL
- *
- * Identical to fdt_get_property(), but only examine the first namelen
- * characters of name for matching the property name.
- *
- * Return: pointer to the structure representing the property, or NULL
- * if not found
- */
-#ifndef SWIG /* Not available in Python */
-const struct fdt_property *fdt_get_property_namelen(const void *fdt,
- int nodeoffset,
- const char *name,
- int namelen, int *lenp);
-static inline struct fdt_property *
-fdt_get_property_namelen_w(void *fdt, int nodeoffset, const char *name,
- int namelen, int *lenp)
-{
- return (struct fdt_property *)(uintptr_t)fdt_get_property_namelen(
- fdt, nodeoffset, name, namelen, lenp);
-}
-#endif
-
-/**
- * fdt_get_property - find a given property in a given node
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to find
- * @name: name of the property to find
- * @lenp: pointer to an integer variable (will be overwritten) or NULL
- *
- * fdt_get_property() retrieves a pointer to the fdt_property
- * structure within the device tree blob corresponding to the property
- * named 'name' of the node at offset nodeoffset. If lenp is
- * non-NULL, the length of the property value is also returned, in the
- * integer pointed to by lenp.
- *
- * returns:
- * pointer to the structure representing the property
- * if lenp is non-NULL, *lenp contains the length of the property
- * value (>=0)
- * NULL, on error
- * if lenp is non-NULL, *lenp contains an error code (<0):
- * -FDT_ERR_NOTFOUND, node does not have named property
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
- * tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset,
- const char *name, int *lenp);
-static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
- const char *name,
- int *lenp)
-{
- return (struct fdt_property *)(uintptr_t)
- fdt_get_property(fdt, nodeoffset, name, lenp);
-}
-
-/**
- * fdt_getprop_by_offset - retrieve the value of a property at a given offset
- * @fdt: pointer to the device tree blob
- * @offset: offset of the property to read
- * @namep: pointer to a string variable (will be overwritten) or NULL
- * @lenp: pointer to an integer variable (will be overwritten) or NULL
- *
- * fdt_getprop_by_offset() retrieves a pointer to the value of the
- * property at structure block offset 'offset' (this will be a pointer
- * to within the device blob itself, not a copy of the value). If
- * lenp is non-NULL, the length of the property value is also
- * returned, in the integer pointed to by lenp. If namep is non-NULL,
- * the property's name will also be returned in the char * pointed to
- * by namep (this will be a pointer to within the device tree's string
- * block, not a new copy of the name).
- *
- * returns:
- * pointer to the property's value
- * if lenp is non-NULL, *lenp contains the length of the property
- * value (>=0)
- * if namep is non-NULL *namep contains a pointer to the property
- * name.
- * NULL, on error
- * if lenp is non-NULL, *lenp contains an error code (<0):
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-#ifndef SWIG /* This function is not useful in Python */
-const void *fdt_getprop_by_offset(const void *fdt, int offset,
- const char **namep, int *lenp);
-#endif
-
-/**
- * fdt_getprop_namelen - get property value based on substring
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to find
- * @name: name of the property to find
- * @namelen: number of characters of name to consider
- * @lenp: pointer to an integer variable (will be overwritten) or NULL
- *
- * Identical to fdt_getprop(), but only examine the first namelen
- * characters of name for matching the property name.
- *
- * Return: pointer to the property's value or NULL on error
- */
-#ifndef SWIG /* Not available in Python */
-const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
- const char *name, int namelen, int *lenp);
-static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
- const char *name, int namelen,
- int *lenp)
-{
- return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name,
- namelen, lenp);
-}
-#endif
-
-/**
- * fdt_getprop - retrieve the value of a given property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to find
- * @name: name of the property to find
- * @lenp: pointer to an integer variable (will be overwritten) or NULL
- *
- * fdt_getprop() retrieves a pointer to the value of the property
- * named @name of the node at offset @nodeoffset (this will be a
- * pointer to within the device blob itself, not a copy of the value).
- * If @lenp is non-NULL, the length of the property value is also
- * returned, in the integer pointed to by @lenp.
- *
- * returns:
- * pointer to the property's value
- * if lenp is non-NULL, *lenp contains the length of the property
- * value (>=0)
- * NULL, on error
- * if lenp is non-NULL, *lenp contains an error code (<0):
- * -FDT_ERR_NOTFOUND, node does not have named property
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
- * tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-const void *fdt_getprop(const void *fdt, int nodeoffset,
- const char *name, int *lenp);
-static inline void *fdt_getprop_w(void *fdt, int nodeoffset,
- const char *name, int *lenp)
-{
- return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp);
-}
-
-/**
- * fdt_get_phandle - retrieve the phandle of a given node
- * @fdt: pointer to the device tree blob
- * @nodeoffset: structure block offset of the node
- *
- * fdt_get_phandle() retrieves the phandle of the device tree node at
- * structure block offset nodeoffset.
- *
- * returns:
- * the phandle of the node at nodeoffset, on success (!= 0, != -1)
- * 0, if the node has no phandle, or another error occurs
- */
-uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
-
-/**
- * fdt_get_alias_namelen - get alias based on substring
- * @fdt: pointer to the device tree blob
- * @name: name of the alias to look up
- * @namelen: number of characters of name to consider
- *
- * Identical to fdt_get_alias(), but only examine the first @namelen
- * characters of @name for matching the alias name.
- *
- * Return: a pointer to the expansion of the alias named @name, if it exists,
- * NULL otherwise
- */
-#ifndef SWIG /* Not available in Python */
-const char *fdt_get_alias_namelen(const void *fdt,
- const char *name, int namelen);
-#endif
-
-/**
- * fdt_get_alias - retrieve the path referenced by a given alias
- * @fdt: pointer to the device tree blob
- * @name: name of the alias to look up
- *
- * fdt_get_alias() retrieves the value of a given alias. That is, the
- * value of the property named @name in the node /aliases.
- *
- * returns:
- * a pointer to the expansion of the alias named 'name', if it exists
- * NULL, if the given alias or the /aliases node does not exist
- */
-const char *fdt_get_alias(const void *fdt, const char *name);
-
-/**
- * fdt_get_symbol_namelen - get symbol based on substring
- * @fdt: pointer to the device tree blob
- * @name: name of the symbol to look up
- * @namelen: number of characters of name to consider
- *
- * Identical to fdt_get_symbol(), but only examine the first @namelen
- * characters of @name for matching the symbol name.
- *
- * Return: a pointer to the expansion of the symbol named @name, if it exists,
- * NULL otherwise
- */
-#ifndef SWIG /* Not available in Python */
-const char *fdt_get_symbol_namelen(const void *fdt,
- const char *name, int namelen);
-#endif
-
-/**
- * fdt_get_symbol - retrieve the path referenced by a given symbol
- * @fdt: pointer to the device tree blob
- * @name: name of the symbol to look up
- *
- * fdt_get_symbol() retrieves the value of a given symbol. That is,
- * the value of the property named @name in the node
- * /__symbols__. Such a node exists only for a device tree blob that
- * has been compiled with the -@ dtc option. Each property corresponds
- * to a label appearing in the device tree source, with the name of
- * the property being the label and the value being the full path of
- * the node it is attached to.
- *
- * returns:
- * a pointer to the expansion of the symbol named 'name', if it exists
- * NULL, if the given symbol or the /__symbols__ node does not exist
- */
-const char *fdt_get_symbol(const void *fdt, const char *name);
-
-/**
- * fdt_get_path - determine the full path of a node
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose path to find
- * @buf: character buffer to contain the returned path (will be overwritten)
- * @buflen: size of the character buffer at buf
- *
- * fdt_get_path() computes the full path of the node at offset
- * nodeoffset, and records that path in the buffer at buf.
- *
- * NOTE: This function is expensive, as it must scan the device tree
- * structure from the start to nodeoffset.
- *
- * returns:
- * 0, on success
- * buf contains the absolute path of the node at
- * nodeoffset, as a NUL-terminated string.
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
- * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
- * characters and will not fit in the given buffer.
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE, standard meanings
- */
-int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
-
-/**
- * fdt_supernode_atdepth_offset - find a specific ancestor of a node
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose parent to find
- * @supernodedepth: depth of the ancestor to find
- * @nodedepth: pointer to an integer variable (will be overwritten) or NULL
- *
- * fdt_supernode_atdepth_offset() finds an ancestor of the given node
- * at a specific depth from the root (where the root itself has depth
- * 0, its immediate subnodes depth 1 and so forth). So
- * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL);
- * will always return 0, the offset of the root node. If the node at
- * nodeoffset has depth D, then:
- * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL);
- * will return nodeoffset itself.
- *
- * NOTE: This function is expensive, as it must scan the device tree
- * structure from the start to nodeoffset.
- *
- * returns:
- * structure block offset of the node at node offset's ancestor
- * of depth supernodedepth (>=0), on success
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
- * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of
- * nodeoffset
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE, standard meanings
- */
-int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
- int supernodedepth, int *nodedepth);
-
-/**
- * fdt_node_depth - find the depth of a given node
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose parent to find
- *
- * fdt_node_depth() finds the depth of a given node. The root node
- * has depth 0, its immediate subnodes depth 1 and so forth.
- *
- * NOTE: This function is expensive, as it must scan the device tree
- * structure from the start to nodeoffset.
- *
- * returns:
- * depth of the node at nodeoffset (>=0), on success
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE, standard meanings
- */
-int fdt_node_depth(const void *fdt, int nodeoffset);
-
-/**
- * fdt_parent_offset - find the parent of a given node
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose parent to find
- *
- * fdt_parent_offset() locates the parent node of a given node (that
- * is, it finds the offset of the node which contains the node at
- * nodeoffset as a subnode).
- *
- * NOTE: This function is expensive, as it must scan the device tree
- * structure from the start to nodeoffset, *twice*.
- *
- * returns:
- * structure block offset of the parent of the node at nodeoffset
- * (>=0), on success
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE, standard meanings
- */
-int fdt_parent_offset(const void *fdt, int nodeoffset);
-
-/**
- * fdt_node_offset_by_prop_value - find nodes with a given property value
- * @fdt: pointer to the device tree blob
- * @startoffset: only find nodes after this offset
- * @propname: property name to check
- * @propval: property value to search for
- * @proplen: length of the value in propval
- *
- * fdt_node_offset_by_prop_value() returns the offset of the first
- * node after startoffset, which has a property named propname whose
- * value is of length proplen and has value equal to propval; or if
- * startoffset is -1, the very first such node in the tree.
- *
- * To iterate through all nodes matching the criterion, the following
- * idiom can be used:
- * offset = fdt_node_offset_by_prop_value(fdt, -1, propname,
- * propval, proplen);
- * while (offset != -FDT_ERR_NOTFOUND) {
- * // other code here
- * offset = fdt_node_offset_by_prop_value(fdt, offset, propname,
- * propval, proplen);
- * }
- *
- * Note the -1 in the first call to the function, if 0 is used here
- * instead, the function will never locate the root node, even if it
- * matches the criterion.
- *
- * returns:
- * structure block offset of the located node (>= 0, >startoffset),
- * on success
- * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
- * tree after startoffset
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE, standard meanings
- */
-int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
- const char *propname,
- const void *propval, int proplen);
-
-/**
- * fdt_node_offset_by_phandle - find the node with a given phandle
- * @fdt: pointer to the device tree blob
- * @phandle: phandle value
- *
- * fdt_node_offset_by_phandle() returns the offset of the node
- * which has the given phandle value. If there is more than one node
- * in the tree with the given phandle (an invalid tree), results are
- * undefined.
- *
- * returns:
- * structure block offset of the located node (>= 0), on success
- * -FDT_ERR_NOTFOUND, no node with that phandle exists
- * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1)
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE, standard meanings
- */
-int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
-
-/**
- * fdt_node_check_compatible - check a node's compatible property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of a tree node
- * @compatible: string to match against
- *
- * fdt_node_check_compatible() returns 0 if the given node contains a
- * @compatible property with the given string as one of its elements,
- * it returns non-zero otherwise, or on error.
- *
- * returns:
- * 0, if the node has a 'compatible' property listing the given string
- * 1, if the node has a 'compatible' property, but it does not list
- * the given string
- * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
- * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE, standard meanings
- */
-int fdt_node_check_compatible(const void *fdt, int nodeoffset,
- const char *compatible);
-
-/**
- * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value
- * @fdt: pointer to the device tree blob
- * @startoffset: only find nodes after this offset
- * @compatible: 'compatible' string to match against
- *
- * fdt_node_offset_by_compatible() returns the offset of the first
- * node after startoffset, which has a 'compatible' property which
- * lists the given compatible string; or if startoffset is -1, the
- * very first such node in the tree.
- *
- * To iterate through all nodes matching the criterion, the following
- * idiom can be used:
- * offset = fdt_node_offset_by_compatible(fdt, -1, compatible);
- * while (offset != -FDT_ERR_NOTFOUND) {
- * // other code here
- * offset = fdt_node_offset_by_compatible(fdt, offset, compatible);
- * }
- *
- * Note the -1 in the first call to the function, if 0 is used here
- * instead, the function will never locate the root node, even if it
- * matches the criterion.
- *
- * returns:
- * structure block offset of the located node (>= 0, >startoffset),
- * on success
- * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
- * tree after startoffset
- * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE, standard meanings
- */
-int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
- const char *compatible);
-
-/**
- * fdt_stringlist_contains - check a string list property for a string
- * @strlist: Property containing a list of strings to check
- * @listlen: Length of property
- * @str: String to search for
- *
- * This is a utility function provided for convenience. The list contains
- * one or more strings, each terminated by \0, as is found in a device tree
- * "compatible" property.
- *
- * Return: 1 if the string is found in the list, 0 not found, or invalid list
- */
-int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
-
-/**
- * fdt_stringlist_count - count the number of strings in a string list
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of a tree node
- * @property: name of the property containing the string list
- *
- * Return:
- * the number of strings in the given property
- * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
- * -FDT_ERR_NOTFOUND if the property does not exist
- */
-int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property);
-
-/**
- * fdt_stringlist_search - find a string in a string list and return its index
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of a tree node
- * @property: name of the property containing the string list
- * @string: string to look up in the string list
- *
- * Note that it is possible for this function to succeed on property values
- * that are not NUL-terminated. That's because the function will stop after
- * finding the first occurrence of @string. This can for example happen with
- * small-valued cell properties, such as #address-cells, when searching for
- * the empty string.
- *
- * return:
- * the index of the string in the list of strings
- * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
- * -FDT_ERR_NOTFOUND if the property does not exist or does not contain
- * the given string
- */
-int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
- const char *string);
-
-/**
- * fdt_stringlist_get() - obtain the string at a given index in a string list
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of a tree node
- * @property: name of the property containing the string list
- * @index: index of the string to return
- * @lenp: return location for the string length or an error code on failure
- *
- * Note that this will successfully extract strings from properties with
- * non-NUL-terminated values. For example on small-valued cell properties
- * this function will return the empty string.
- *
- * If non-NULL, the length of the string (on success) or a negative error-code
- * (on failure) will be stored in the integer pointer to by lenp.
- *
- * Return:
- * A pointer to the string at the given index in the string list or NULL on
- * failure. On success the length of the string will be stored in the memory
- * location pointed to by the lenp parameter, if non-NULL. On failure one of
- * the following negative error codes will be returned in the lenp parameter
- * (if non-NULL):
- * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
- * -FDT_ERR_NOTFOUND if the property does not exist
- */
-const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
- const char *property, int index,
- int *lenp);
-
-/**********************************************************************/
-/* Read-only functions (addressing related) */
-/**********************************************************************/
-
-/**
- * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells
- *
- * This is the maximum value for #address-cells, #size-cells and
- * similar properties that will be processed by libfdt. IEE1275
- * requires that OF implementations handle values up to 4.
- * Implementations may support larger values, but in practice higher
- * values aren't used.
- */
-#define FDT_MAX_NCELLS 4
-
-/**
- * fdt_address_cells - retrieve address size for a bus represented in the tree
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node to find the address size for
- *
- * When the node has a valid #address-cells property, returns its value.
- *
- * returns:
- * 0 <= n < FDT_MAX_NCELLS, on success
- * 2, if the node has no #address-cells property
- * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
- * #address-cells property
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_address_cells(const void *fdt, int nodeoffset);
-
-/**
- * fdt_size_cells - retrieve address range size for a bus represented in the
- * tree
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node to find the address range size for
- *
- * When the node has a valid #size-cells property, returns its value.
- *
- * returns:
- * 0 <= n < FDT_MAX_NCELLS, on success
- * 1, if the node has no #size-cells property
- * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
- * #size-cells property
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_size_cells(const void *fdt, int nodeoffset);
-
-
-/**********************************************************************/
-/* Write-in-place functions */
-/**********************************************************************/
-
-/**
- * fdt_setprop_inplace_namelen_partial - change a property's value,
- * but not its size
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @namelen: number of characters of name to consider
- * @idx: index of the property to change in the array
- * @val: pointer to data to replace the property value with
- * @len: length of the property value
- *
- * Identical to fdt_setprop_inplace(), but modifies the given property
- * starting from the given index, and using only the first characters
- * of the name. It is useful when you want to manipulate only one value of
- * an array and you have a string that doesn't end with \0.
- *
- * Return: 0 on success, negative libfdt error value otherwise
- */
-#ifndef SWIG /* Not available in Python */
-int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
- const char *name, int namelen,
- uint32_t idx, const void *val,
- int len);
-#endif
-
-/**
- * fdt_setprop_inplace - change a property's value, but not its size
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: pointer to data to replace the property value with
- * @len: length of the property value
- *
- * fdt_setprop_inplace() replaces the value of a given property with
- * the data in val, of length len. This function cannot change the
- * size of a property, and so will only work if len is equal to the
- * current length of the property.
- *
- * This function will alter only the bytes in the blob which contain
- * the given property value, and will not alter or move any other part
- * of the tree.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if len is not equal to the property's current length
- * -FDT_ERR_NOTFOUND, node does not have the named property
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-#ifndef SWIG /* Not available in Python */
-int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
- const void *val, int len);
-#endif
-
-/**
- * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: 32-bit integer value to replace the property with
- *
- * fdt_setprop_inplace_u32() replaces the value of a given property
- * with the 32-bit integer value in val, converting val to big-endian
- * if necessary. This function cannot change the size of a property,
- * and so will only work if the property already exists and has length
- * 4.
- *
- * This function will alter only the bytes in the blob which contain
- * the given property value, and will not alter or move any other part
- * of the tree.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if the property's length is not equal to 4
- * -FDT_ERR_NOTFOUND, node does not have the named property
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset,
- const char *name, uint32_t val)
-{
- fdt32_t tmp = cpu_to_fdt32(val);
- return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
-}
-
-/**
- * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: 64-bit integer value to replace the property with
- *
- * fdt_setprop_inplace_u64() replaces the value of a given property
- * with the 64-bit integer value in val, converting val to big-endian
- * if necessary. This function cannot change the size of a property,
- * and so will only work if the property already exists and has length
- * 8.
- *
- * This function will alter only the bytes in the blob which contain
- * the given property value, and will not alter or move any other part
- * of the tree.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if the property's length is not equal to 8
- * -FDT_ERR_NOTFOUND, node does not have the named property
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset,
- const char *name, uint64_t val)
-{
- fdt64_t tmp = cpu_to_fdt64(val);
- return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
-}
-
-/**
- * fdt_setprop_inplace_cell - change the value of a single-cell property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node containing the property
- * @name: name of the property to change the value of
- * @val: new value of the 32-bit cell
- *
- * This is an alternative name for fdt_setprop_inplace_u32()
- * Return: 0 on success, negative libfdt error number otherwise.
- */
-static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
- const char *name, uint32_t val)
-{
- return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val);
-}
-
-/**
- * fdt_nop_property - replace a property with nop tags
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to nop
- * @name: name of the property to nop
- *
- * fdt_nop_property() will replace a given property's representation
- * in the blob with FDT_NOP tags, effectively removing it from the
- * tree.
- *
- * This function will alter only the bytes in the blob which contain
- * the property, and will not alter or move any other part of the
- * tree.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOTFOUND, node does not have the named property
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_nop_property(void *fdt, int nodeoffset, const char *name);
-
-/**
- * fdt_nop_node - replace a node (subtree) with nop tags
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node to nop
- *
- * fdt_nop_node() will replace a given node's representation in the
- * blob, including all its subnodes, if any, with FDT_NOP tags,
- * effectively removing it from the tree.
- *
- * This function will alter only the bytes in the blob which contain
- * the node and its properties and subnodes, and will not alter or
- * move any other part of the tree.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_nop_node(void *fdt, int nodeoffset);
-
-/**********************************************************************/
-/* Sequential write functions */
-/**********************************************************************/
-
-/* fdt_create_with_flags flags */
-#define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1
- /* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property
- * names in the fdt. This can result in faster creation times, but
- * a larger fdt. */
-
-#define FDT_CREATE_FLAGS_ALL (FDT_CREATE_FLAG_NO_NAME_DEDUP)
-
-/**
- * fdt_create_with_flags - begin creation of a new fdt
- * @buf: pointer to memory allocated where fdt will be created
- * @bufsize: size of the memory space at fdt
- * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0.
- *
- * fdt_create_with_flags() begins the process of creating a new fdt with
- * the sequential write interface.
- *
- * fdt creation process must end with fdt_finish() to produce a valid fdt.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt
- * -FDT_ERR_BADFLAGS, flags is not valid
- */
-int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags);
-
-/**
- * fdt_create - begin creation of a new fdt
- * @buf: pointer to memory allocated where fdt will be created
- * @bufsize: size of the memory space at fdt
- *
- * fdt_create() is equivalent to fdt_create_with_flags() with flags=0.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt
- */
-int fdt_create(void *buf, int bufsize);
-
-/**
- * fdt_resize - move and resize a device tree in sequential write state
- * @fdt: Pointer to the device tree to resize
- * @buf: Buffer where resized tree should be placed
- * @bufsize: Size of the buffer at @buf
- *
- * fdt_resize() moves the device tree blob from @fdt to @buf and
- * resizes it to fit in the new buffer size.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if @bufsize is too small
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE, standard meanings
- */
-int fdt_resize(void *fdt, void *buf, int bufsize);
-
-/**
- * fdt_add_reservemap_entry - add an entry to the memory reserve map
- * @fdt: Pointer to the device tree blob
- * @addr: Start address of the reserve map entry
- * @size: Size of the reserved region
- *
- * fdt_add_reservemap_entry() adds a memory reserve map entry to the
- * device tree blob during the sequential write process. This function
- * can only be called after fdt_create() and before fdt_finish_reservemap().
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if there is insufficient space in the blob
- * -FDT_ERR_BADSTATE, if not in the correct sequential write state
- */
-int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size);
-
-/**
- * fdt_finish_reservemap - complete the memory reserve map
- * @fdt: Pointer to the device tree blob
- *
- * fdt_finish_reservemap() completes the memory reserve map section
- * of the device tree blob during sequential write. After calling this
- * function, no more reserve map entries can be added and the blob
- * moves to the structure creation phase.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADSTATE, if not in the correct sequential write state
- */
-int fdt_finish_reservemap(void *fdt);
-
-/**
- * fdt_begin_node - start creation of a new node
- * @fdt: Pointer to the device tree blob
- * @name: Name of the node to create
- *
- * fdt_begin_node() starts the creation of a new node with the given
- * @name during sequential write. After calling this function, properties
- * can be added with fdt_property() and subnodes can be created with
- * additional fdt_begin_node() calls. The node must be completed with
- * fdt_end_node().
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if there is insufficient space in the blob
- * -FDT_ERR_BADSTATE, if not in the correct sequential write state
- */
-int fdt_begin_node(void *fdt, const char *name);
-
-/**
- * fdt_property - add a property to the current node
- * @fdt: Pointer to the device tree blob
- * @name: Name of the property to add
- * @val: Pointer to the property value
- * @len: Length of the property value in bytes
- *
- * fdt_property() adds a property with the given @name and value to
- * the current node during sequential write. This function can only
- * be called between fdt_begin_node() and fdt_end_node().
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if there is insufficient space in the blob
- * -FDT_ERR_BADSTATE, if not currently within a node
- */
-int fdt_property(void *fdt, const char *name, const void *val, int len);
-static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
-{
- fdt32_t tmp = cpu_to_fdt32(val);
- return fdt_property(fdt, name, &tmp, sizeof(tmp));
-}
-static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val)
-{
- fdt64_t tmp = cpu_to_fdt64(val);
- return fdt_property(fdt, name, &tmp, sizeof(tmp));
-}
-
-#ifndef SWIG /* Not available in Python */
-static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
-{
- return fdt_property_u32(fdt, name, val);
-}
-#endif
-
-/**
- * fdt_property_placeholder - add a new property and return a ptr to its value
- *
- * @fdt: pointer to the device tree blob
- * @name: name of property to add
- * @len: length of property value in bytes
- * @valp: returns a pointer to where the value should be placed
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_NOSPACE, standard meanings
- */
-int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp);
-
-#define fdt_property_string(fdt, name, str) \
- fdt_property(fdt, name, str, strlen(str)+1)
-
-/**
- * fdt_end_node - complete the current node
- * @fdt: Pointer to the device tree blob
- *
- * fdt_end_node() completes the current node during sequential write. This
- * function must be called to close each node started with
- * fdt_begin_node(). After calling this function, no more properties or subnodes
- * can be added to the node.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADSTATE, if not currently within a node
- */
-int fdt_end_node(void *fdt);
-
-/**
- * fdt_finish - complete device tree creation
- * @fdt: Pointer to the device tree blob
- *
- * fdt_finish() completes the device tree creation process started with
- * fdt_create(). This function finalizes the device tree blob and makes it ready
- * for use. After calling this function, the blob is complete and can be used
- * with libfdt read-only and read-write functions, but not with sequential write
- * functions.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADSTATE, if the sequential write process is incomplete
- */
-int fdt_finish(void *fdt);
-
-/**********************************************************************/
-/* Read-write functions */
-/**********************************************************************/
-
-/**
- * fdt_create_empty_tree - create an empty device tree
- * @buf: Buffer where the empty tree should be created
- * @bufsize: Size of the buffer at @buf
- *
- * fdt_create_empty_tree() creates a minimal empty device tree blob
- * in the given buffer. The tree contains only a root node with no
- * properties or subnodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if @bufsize is too small for even an empty tree
- */
-int fdt_create_empty_tree(void *buf, int bufsize);
-
-/**
- * fdt_open_into - move a device tree into a new buffer and make editable
- * @fdt: Pointer to the device tree to move
- * @buf: Buffer where the editable tree should be placed
- * @bufsize: Size of the buffer at @buf
- *
- * fdt_open_into() moves and reorganizes the device tree blob from @fdt
- * into @buf, converting it to a format suitable for read-write operations.
- * The new buffer should allow space for modifications.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, if @bufsize is too small
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_open_into(const void *fdt, void *buf, int bufsize);
-
-/**
- * fdt_pack - pack a device tree blob
- * @fdt: Pointer to the device tree blob
- *
- * fdt_pack() reorganizes the device tree blob to eliminate any free space
- * and pack it into the minimum possible size. This is useful after making
- * modifications that might have left gaps in the blob.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT, standard meanings
- */
-int fdt_pack(void *fdt);
-
-/**
- * fdt_add_mem_rsv - add one memory reserve map entry
- * @fdt: pointer to the device tree blob
- * @address: 64-bit start address of the reserve map entry
- * @size: 64-bit size of the reserved region
- *
- * Adds a reserve map entry to the given blob reserving a region at
- * address address of length size.
- *
- * This function will insert data into the reserve map and will
- * therefore change the indexes of some entries in the table.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new reservation entry
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size);
-
-/**
- * fdt_del_mem_rsv - remove a memory reserve map entry
- * @fdt: pointer to the device tree blob
- * @n: entry to remove
- *
- * fdt_del_mem_rsv() removes the n-th memory reserve map entry from
- * the blob.
- *
- * This function will delete data from the reservation table and will
- * therefore change the indexes of some entries in the table.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there
- * are less than n+1 reserve map entries)
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_del_mem_rsv(void *fdt, int n);
-
-/**
- * fdt_set_name - change the name of a given node
- * @fdt: pointer to the device tree blob
- * @nodeoffset: structure block offset of a node
- * @name: name to give the node
- *
- * fdt_set_name() replaces the name (including unit address, if any)
- * of the given node with the given string. NOTE: this function can't
- * efficiently check if the new name is unique amongst the given
- * node's siblings; results are undefined if this function is invoked
- * with a name equal to one of the given node's siblings.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob
- * to contain the new name
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE, standard meanings
- */
-int fdt_set_name(void *fdt, int nodeoffset, const char *name);
-
-/**
- * fdt_setprop_namelen - create or change a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @namelen: length of the name
- * @val: pointer to data to set the property value to
- * @len: length of the property value
- *
- * fdt_setprop_namelen() sets the value of the named property in the given
- * node to the given value and length, creating the property if it
- * does not already exist.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
- int namelen, const void *val, int len);
-
-/**
- * fdt_setprop - create or change a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: pointer to data to set the property value to
- * @len: length of the property value
- *
- * fdt_setprop() sets the value of the named property in the given
- * node to the given value and length, creating the property if it
- * does not already exist.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-static inline int fdt_setprop(void *fdt, int nodeoffset, const char *name,
- const void *val, int len)
-{
- return fdt_setprop_namelen(fdt, nodeoffset, name, strlen(name), val,
- len);
-}
-
-/**
- * fdt_setprop_placeholder_namelen - allocate space for a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @namelen: length of the name
- * @len: length of the property value
- * @prop_data: return pointer to property data
- *
- * fdt_setprop_placeholder_namelen() allocates the named property in the given node.
- * If the property exists it is resized. In either case a pointer to the
- * property data is returned.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
- int namelen, int len, void **prop_data);
-
-/**
- * fdt_setprop_placeholder - allocate space for a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @len: length of the property value
- * @prop_data: return pointer to property data
- *
- * fdt_setprop_placeholder() allocates the named property in the given node.
- * If the property exists it is resized. In either case a pointer to the
- * property data is returned.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-static inline int fdt_setprop_placeholder(void *fdt, int nodeoffset,
- const char *name, int len,
- void **prop_data)
-{
- return fdt_setprop_placeholder_namelen(fdt, nodeoffset, name,
- strlen(name), len, prop_data);
-}
-
-/**
- * fdt_setprop_u32 - set a property to a 32-bit integer
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: 32-bit integer value for the property (native endian)
- *
- * fdt_setprop_u32() sets the value of the named property in the given
- * node to the given 32-bit integer value (converting to big-endian if
- * necessary), or creates a new property with that value if it does
- * not already exist.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name,
- uint32_t val)
-{
- fdt32_t tmp = cpu_to_fdt32(val);
- return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
-}
-
-/**
- * fdt_setprop_u64 - set a property to a 64-bit integer
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: 64-bit integer value for the property (native endian)
- *
- * fdt_setprop_u64() sets the value of the named property in the given
- * node to the given 64-bit integer value (converting to big-endian if
- * necessary), or creates a new property with that value if it does
- * not already exist.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name,
- uint64_t val)
-{
- fdt64_t tmp = cpu_to_fdt64(val);
- return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
-}
-
-/**
- * fdt_setprop_cell - set a property to a single cell value
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: 32-bit integer value for the property (native endian)
- *
- * This is an alternative name for fdt_setprop_u32()
- *
- * Return: 0 on success, negative libfdt error value otherwise.
- */
-static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
- uint32_t val)
-{
- return fdt_setprop_u32(fdt, nodeoffset, name, val);
-}
-
-/**
- * fdt_setprop_string - set a property to a string value
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @str: string value for the property
- *
- * fdt_setprop_string() sets the value of the named property in the
- * given node to the given string value (using the length of the
- * string to determine the new length of the property), or creates a
- * new property with that value if it does not already exist.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-#define fdt_setprop_string(fdt, nodeoffset, name, str) \
- fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
-
-/**
- * fdt_setprop_namelen_string - set a property to a string value
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @namelen: number of characters of name to consider
- * @str: string value for the property
- *
- * fdt_setprop_namelen_string() sets the value of the named property in the
- * given node to the given string value (using the length of the
- * string to determine the new length of the property), or creates a
- * new property with that value if it does not already exist.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-#define fdt_setprop_namelen_string(fdt, nodeoffset, name, namelen, str) \
- fdt_setprop_namelen((fdt), (nodeoffset), (name), (namelen), (str), \
- strlen(str) + 1)
-
-/**
- * fdt_setprop_empty - set a property to an empty value
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- *
- * fdt_setprop_empty() sets the value of the named property in the
- * given node to an empty (zero length) value, or creates a new empty
- * property if it does not already exist.
- *
- * This function may insert or delete data from the blob, and will
- * therefore change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-#define fdt_setprop_empty(fdt, nodeoffset, name) \
- fdt_setprop((fdt), (nodeoffset), (name), NULL, 0)
-
-/**
- * fdt_appendprop - append to or create a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to append to
- * @val: pointer to data to append to the property value
- * @len: length of the data to append to the property value
- *
- * fdt_appendprop() appends the value to the named property in the
- * given node, creating the property if it does not already exist.
- *
- * This function may insert data into the blob, and will therefore
- * change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
- const void *val, int len);
-
-/**
- * fdt_appendprop_u32 - append a 32-bit integer value to a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: 32-bit integer value to append to the property (native endian)
- *
- * fdt_appendprop_u32() appends the given 32-bit integer value
- * (converting to big-endian if necessary) to the value of the named
- * property in the given node, or creates a new property with that
- * value if it does not already exist.
- *
- * This function may insert data into the blob, and will therefore
- * change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-static inline int fdt_appendprop_u32(void *fdt, int nodeoffset,
- const char *name, uint32_t val)
-{
- fdt32_t tmp = cpu_to_fdt32(val);
- return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
-}
-
-/**
- * fdt_appendprop_u64 - append a 64-bit integer value to a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: 64-bit integer value to append to the property (native endian)
- *
- * fdt_appendprop_u64() appends the given 64-bit integer value
- * (converting to big-endian if necessary) to the value of the named
- * property in the given node, or creates a new property with that
- * value if it does not already exist.
- *
- * This function may insert data into the blob, and will therefore
- * change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-static inline int fdt_appendprop_u64(void *fdt, int nodeoffset,
- const char *name, uint64_t val)
-{
- fdt64_t tmp = cpu_to_fdt64(val);
- return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
-}
-
-/**
- * fdt_appendprop_cell - append a single cell value to a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @val: 32-bit integer value to append to the property (native endian)
- *
- * This is an alternative name for fdt_appendprop_u32()
- *
- * Return: 0 on success, negative libfdt error value otherwise.
- */
-static inline int fdt_appendprop_cell(void *fdt, int nodeoffset,
- const char *name, uint32_t val)
-{
- return fdt_appendprop_u32(fdt, nodeoffset, name, val);
-}
-
-/**
- * fdt_appendprop_string - append a string to a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to change
- * @name: name of the property to change
- * @str: string value to append to the property
- *
- * fdt_appendprop_string() appends the given string to the value of
- * the named property in the given node, or creates a new property
- * with that value if it does not already exist.
- *
- * This function may insert data into the blob, and will therefore
- * change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain the new property value
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-#define fdt_appendprop_string(fdt, nodeoffset, name, str) \
- fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
-
-/**
- * fdt_appendprop_addrrange - append a address range property
- * @fdt: pointer to the device tree blob
- * @parent: offset of the parent node
- * @nodeoffset: offset of the node to add a property at
- * @name: name of property
- * @addr: start address of a given range
- * @size: size of a given range
- *
- * fdt_appendprop_addrrange() appends an address range value (start
- * address and size) to the value of the named property in the given
- * node, or creates a new property with that value if it does not
- * already exist.
- *
- * Cell sizes are determined by parent's #address-cells and #size-cells.
- *
- * This function may insert data into the blob, and will therefore
- * change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
- * #address-cells property
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size
- * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
- * contain a new property
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
- const char *name, uint64_t addr, uint64_t size);
-
-/**
- * fdt_delprop - delete a property
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node whose property to nop
- * @name: name of the property to nop
- *
- * fdt_delprop() will delete the given property.
- *
- * This function will delete data from the blob, and will therefore
- * change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOTFOUND, node does not have the named property
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_delprop(void *fdt, int nodeoffset, const char *name);
-
-/**
- * fdt_add_subnode_namelen - creates a new node based on substring
- * @fdt: pointer to the device tree blob
- * @parentoffset: structure block offset of a node
- * @name: name of the subnode to create
- * @namelen: number of characters of name to consider
- *
- * Identical to fdt_add_subnode(), but use only the first @namelen
- * characters of @name as the name of the new node. This is useful for
- * creating subnodes based on a portion of a larger string, such as a
- * full path.
- *
- * Return: structure block offset of the created subnode (>=0),
- * negative libfdt error value otherwise
- */
-#ifndef SWIG /* Not available in Python */
-int fdt_add_subnode_namelen(void *fdt, int parentoffset,
- const char *name, int namelen);
-#endif
-
-/**
- * fdt_add_subnode - creates a new node
- * @fdt: pointer to the device tree blob
- * @parentoffset: structure block offset of a node
- * @name: name of the subnode to locate
- *
- * fdt_add_subnode() creates a new node as a subnode of the node at
- * structure block offset parentoffset, with the given name (which
- * should include the unit address, if any).
- *
- * This function will insert data into the blob, and will therefore
- * change the offsets of some existing nodes.
- *
- * returns:
- * structure block offset of the created subnode (>=0), on success
- * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
- * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
- * tag
- * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
- * the given name
- * -FDT_ERR_NOSPACE, if there is insufficient free space in the
- * blob to contain the new node
- * -FDT_ERR_NOSPACE
- * -FDT_ERR_BADLAYOUT
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings.
- */
-int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
-
-/**
- * fdt_del_node - delete a node (subtree)
- * @fdt: pointer to the device tree blob
- * @nodeoffset: offset of the node to nop
- *
- * fdt_del_node() will remove the given node, including all its
- * subnodes if any, from the blob.
- *
- * This function will delete data from the blob, and will therefore
- * change the offsets of some existing nodes.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_del_node(void *fdt, int nodeoffset);
-
-/**
- * fdt_overlay_apply - Applies a DT overlay on a base DT
- * @fdt: pointer to the base device tree blob
- * @fdto: pointer to the device tree overlay blob
- *
- * fdt_overlay_apply() will apply the given device tree overlay on the
- * given base device tree.
- *
- * Expect the base device tree to be modified, even if the function
- * returns an error.
- *
- * returns:
- * 0, on success
- * -FDT_ERR_NOSPACE, there's not enough space in the base device tree
- * -FDT_ERR_NOTFOUND, the overlay points to some nonexistent nodes or
- * properties in the base DT
- * -FDT_ERR_BADPHANDLE,
- * -FDT_ERR_BADOVERLAY,
- * -FDT_ERR_NOPHANDLES,
- * -FDT_ERR_INTERNAL,
- * -FDT_ERR_BADLAYOUT,
- * -FDT_ERR_BADMAGIC,
- * -FDT_ERR_BADOFFSET,
- * -FDT_ERR_BADPATH,
- * -FDT_ERR_BADVERSION,
- * -FDT_ERR_BADSTRUCTURE,
- * -FDT_ERR_BADSTATE,
- * -FDT_ERR_TRUNCATED, standard meanings
- */
-int fdt_overlay_apply(void *fdt, void *fdto);
-
-/**
- * fdt_overlay_target_offset - retrieves the offset of a fragment's target
- * @fdt: Base device tree blob
- * @fdto: Device tree overlay blob
- * @fragment_offset: node offset of the fragment in the overlay
- * @pathp: pointer which receives the path of the target (or NULL)
- *
- * fdt_overlay_target_offset() retrieves the target offset in the base
- * device tree of a fragment, no matter how the actual targeting is
- * done (through a phandle or a path)
- *
- * returns:
- * the targeted node offset in the base device tree
- * Negative error code on error
- */
-int fdt_overlay_target_offset(const void *fdt, const void *fdto,
- int fragment_offset, char const **pathp);
-
-/**********************************************************************/
-/* Debugging / informational functions */
-/**********************************************************************/
-
-/**
- * fdt_strerror - return string description of error code
- * @errval: Error code returned by a libfdt function
- *
- * fdt_strerror() returns a string description of the error code passed
- * in @errval.
- *
- * returns:
- * pointer to a string describing the error code
- */
-const char *fdt_strerror(int errval);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBFDT_H */
diff --git a/na/scripts/dtc/libfdt/libfdt_env.h b/na/scripts/dtc/libfdt/libfdt_env.h
deleted file mode 100644
index 5580b48..0000000
--- a/na/scripts/dtc/libfdt/libfdt_env.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
-#ifndef LIBFDT_ENV_H
-#define LIBFDT_ENV_H
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- * Copyright 2012 Kim Phillips, Freescale Semiconductor.
- */
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#ifdef __CHECKER__
-#define FDT_FORCE __attribute__((force))
-#define FDT_BITWISE __attribute__((bitwise))
-#else
-#define FDT_FORCE
-#define FDT_BITWISE
-#endif
-
-typedef uint16_t FDT_BITWISE fdt16_t;
-typedef uint32_t FDT_BITWISE fdt32_t;
-typedef uint64_t FDT_BITWISE fdt64_t;
-
-#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
-#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
-#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
- (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
-#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
- (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
- (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
- (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
-
-static inline uint16_t fdt16_to_cpu(fdt16_t x)
-{
- return (FDT_FORCE uint16_t)CPU_TO_FDT16(x);
-}
-static inline fdt16_t cpu_to_fdt16(uint16_t x)
-{
- return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x);
-}
-
-static inline uint32_t fdt32_to_cpu(fdt32_t x)
-{
- return (FDT_FORCE uint32_t)CPU_TO_FDT32(x);
-}
-static inline fdt32_t cpu_to_fdt32(uint32_t x)
-{
- return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x);
-}
-
-static inline uint64_t fdt64_to_cpu(fdt64_t x)
-{
- return (FDT_FORCE uint64_t)CPU_TO_FDT64(x);
-}
-static inline fdt64_t cpu_to_fdt64(uint64_t x)
-{
- return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x);
-}
-#undef CPU_TO_FDT64
-#undef CPU_TO_FDT32
-#undef CPU_TO_FDT16
-#undef EXTRACT_BYTE
-
-#endif /* LIBFDT_ENV_H */
diff --git a/na/scripts/dtc/libfdt/libfdt_internal.h b/na/scripts/dtc/libfdt/libfdt_internal.h
deleted file mode 100644
index 0e103ca..0000000
--- a/na/scripts/dtc/libfdt/libfdt_internal.h
+++ /dev/null
@@ -1,202 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
-#ifndef LIBFDT_INTERNAL_H
-#define LIBFDT_INTERNAL_H
-/*
- * libfdt - Flat Device Tree manipulation
- * Copyright (C) 2006 David Gibson, IBM Corporation.
- */
-#include <fdt.h>
-
-#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
-#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
-
-int32_t fdt_ro_probe_(const void *fdt);
-#define FDT_RO_PROBE(fdt) \
- { \
- if (!can_assume(VALID_DTB)) { \
- int32_t totalsize_; \
- if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \
- return totalsize_; \
- } \
- }
-
-int fdt_check_node_offset_(const void *fdt, int offset);
-int fdt_check_prop_offset_(const void *fdt, int offset);
-
-const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
- int s_len);
-static inline const char *fdt_find_string_(const char *strtab, int tabsize,
- const char *s)
-{
- return fdt_find_string_len_(strtab, tabsize, s, strlen(s));
-}
-
-int fdt_node_end_offset_(void *fdt, int nodeoffset);
-
-static inline const void *fdt_offset_ptr_(const void *fdt, int offset)
-{
- return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
-}
-
-static inline void *fdt_offset_ptr_w_(void *fdt, int offset)
-{
- return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset);
-}
-
-static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n)
-{
- const struct fdt_reserve_entry *rsv_table =
- (const struct fdt_reserve_entry *)
- ((const char *)fdt + fdt_off_mem_rsvmap(fdt));
-
- return rsv_table + n;
-}
-static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n)
-{
- return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n);
-}
-
-/*
- * Internal helpers to access structural elements of the device tree
- * blob (rather than for example reading integers from within property
- * values). We assume that we are either given a naturally aligned
- * address for the platform or if we are not, we are on a platform
- * where unaligned memory reads will be handled in a graceful manner.
- * If not the external helpers fdtXX_ld() from libfdt.h can be used
- * instead.
- */
-static inline uint32_t fdt32_ld_(const fdt32_t *p)
-{
- return fdt32_to_cpu(*p);
-}
-
-static inline uint64_t fdt64_ld_(const fdt64_t *p)
-{
- return fdt64_to_cpu(*p);
-}
-
-#define FDT_SW_MAGIC (~FDT_MAGIC)
-
-/**********************************************************************/
-/* Checking controls */
-/**********************************************************************/
-
-#ifndef FDT_ASSUME_MASK
-#define FDT_ASSUME_MASK 0
-#endif
-
-/*
- * Defines assumptions which can be enabled. Each of these can be enabled
- * individually. For maximum safety, don't enable any assumptions!
- *
- * For minimal code size and no safety, use ASSUME_PERFECT at your own risk.
- * You should have another method of validating the device tree, such as a
- * signature or hash check before using libfdt.
- *
- * For situations where security is not a concern it may be safe to enable
- * ASSUME_PERFECT.
- */
-enum {
- /*
- * This does essentially no checks. Only the latest device-tree
- * version is correctly handled. Inconsistencies or errors in the device
- * tree may cause undefined behaviour or crashes. Invalid parameters
- * passed to libfdt may do the same.
- *
- * If an error occurs when modifying the tree it may leave the tree in
- * an intermediate (but valid) state. As an example, adding a property
- * where there is insufficient space may result in the property name
- * being added to the string table even though the property itself is
- * not added to the struct section.
- *
- * Only use this if you have a fully validated device tree with
- * the latest supported version and wish to minimise code size.
- */
- ASSUME_PERFECT = 0xff,
-
- /*
- * This assumes that the device tree is sane. i.e. header metadata
- * and basic hierarchy are correct.
- *
- * With this assumption enabled, normal device trees produced by libfdt
- * and the compiler should be handled safely. Malicious device trees and
- * complete garbage may cause libfdt to behave badly or crash. Truncated
- * device trees (e.g. those only partially loaded) can also cause
- * problems.
- *
- * Note: Only checks that relate exclusively to the device tree itself
- * (not the parameters passed to libfdt) are disabled by this
- * assumption. This includes checking headers, tags and the like.
- */
- ASSUME_VALID_DTB = 1 << 0,
-
- /*
- * This builds on ASSUME_VALID_DTB and further assumes that libfdt
- * functions are called with valid parameters, i.e. not trigger
- * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any
- * extensive checking of parameters and the device tree, making various
- * assumptions about correctness.
- *
- * It doesn't make sense to enable this assumption unless
- * ASSUME_VALID_DTB is also enabled.
- */
- ASSUME_VALID_INPUT = 1 << 1,
-
- /*
- * This disables checks for device-tree version and removes all code
- * which handles older versions.
- *
- * Only enable this if you know you have a device tree with the latest
- * version.
- */
- ASSUME_LATEST = 1 << 2,
-
- /*
- * This assumes that it is OK for a failed addition to the device tree,
- * due to lack of space or some other problem, to skip any rollback
- * steps (such as dropping the property name from the string table).
- * This is safe to enable in most circumstances, even though it may
- * leave the tree in a sub-optimal state.
- */
- ASSUME_NO_ROLLBACK = 1 << 3,
-
- /*
- * This assumes that the device tree components appear in a 'convenient'
- * order, i.e. the memory reservation block first, then the structure
- * block and finally the string block.
- *
- * This order is not specified by the device-tree specification,
- * but is expected by libfdt. The device-tree compiler always created
- * device trees with this order.
- *
- * This assumption disables a check in fdt_open_into() and removes the
- * ability to fix the problem there. This is safe if you know that the
- * device tree is correctly ordered. See fdt_blocks_misordered_().
- */
- ASSUME_LIBFDT_ORDER = 1 << 4,
-
- /*
- * This assumes that libfdt itself does not have any internal bugs. It
- * drops certain checks that should never be needed unless libfdt has an
- * undiscovered bug.
- *
- * This can generally be considered safe to enable.
- */
- ASSUME_LIBFDT_FLAWLESS = 1 << 5,
-};
-
-/**
- * can_assume_() - check if a particular assumption is enabled
- *
- * @mask: Mask to check (ASSUME_...)
- * @return true if that assumption is enabled, else false
- */
-static inline bool can_assume_(int mask)
-{
- return FDT_ASSUME_MASK & mask;
-}
-
-/** helper macros for checking assumptions */
-#define can_assume(_assume) can_assume_(ASSUME_ ## _assume)
-
-#endif /* LIBFDT_INTERNAL_H */
diff --git a/na/scripts/extract-asm-offsets.sed b/na/scripts/extract-asm-offsets.sed
deleted file mode 100644
index c97ec2c..0000000
--- a/na/scripts/extract-asm-offsets.sed
+++ /dev/null
@@ -1,18 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Extract `->SYM val COMMENT` markers emitted by inline asm in
-# asm-offsets.c into proper `#define SYM val /* COMMENT */` lines.
-# 1:1 with Linux scripts/Makefile.lib `sed-offsets`.
-#
-# Input : assembly source produced by `gcc -S asm-offsets.c` —
-# inline asm emits lines like
-# .ascii "->KERNEL_MAP_VIRT_ADDR 0 offsetof(...)"
-# Output : header fragment for include/generated/asm-offsets.h
-#
-# Step 1 unwraps the `.ascii "..."` directive so the actual payload
-# (starting with `->`) is exposed. Steps 2-4 then rewrite into a
-# proper #define.
-s:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:
-/^->/{s:->#\(.*\):/* \1 */:;
- s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;
- s:->::;
- p;}
diff --git a/na/scripts/gen_initramfs.sh b/na/scripts/gen_initramfs.sh
deleted file mode 100755
index 7eba2fd..0000000
--- a/na/scripts/gen_initramfs.sh
+++ /dev/null
@@ -1,250 +0,0 @@
-#!/bin/sh
-# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
-# Copyright (C) 2006 Sam Ravnborg <sam@ravnborg.org>
-#
-# Released under the terms of the GNU GPL
-#
-# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
-# the cpio archive.
-# This script assumes that gen_init_cpio is located in usr/ directory
-
-# error out on errors
-set -e
-
-usage() {
-cat << EOF
-Usage:
-$0 [-o <file>] [-l <dep_list>] [-u <uid>] [-g <gid>] {-d | <cpio_source>} ...
- -o <file> Create initramfs file named <file> by using gen_init_cpio
- -l <dep_list> Create dependency list named <dep_list>
- -u <uid> User ID to map to user ID 0 (root).
- <uid> is only meaningful if <cpio_source> is a
- directory. "squash" forces all files to uid 0.
- -g <gid> Group ID to map to group ID 0 (root).
- <gid> is only meaningful if <cpio_source> is a
- directory. "squash" forces all files to gid 0.
- -d <date> Use date for all file mtime values
- <cpio_source> File list or directory for cpio archive.
- If <cpio_source> is a .cpio file it will be used
- as direct input to initramfs.
-
-All options except -o and -l may be repeated and are interpreted
-sequentially and immediately. -u and -g states are preserved across
-<cpio_source> options so an explicit "-u 0 -g 0" is required
-to reset the root/group mapping.
-EOF
-}
-
-# awk style field access
-# $1 - field number; rest is argument string
-field() {
- shift $1 ; echo $1
-}
-
-filetype() {
- local argv1="$1"
-
- # symlink test must come before file test
- if [ -L "${argv1}" ]; then
- echo "slink"
- elif [ -f "${argv1}" ]; then
- echo "file"
- elif [ -d "${argv1}" ]; then
- echo "dir"
- elif [ -b "${argv1}" -o -c "${argv1}" ]; then
- echo "nod"
- elif [ -p "${argv1}" ]; then
- echo "pipe"
- elif [ -S "${argv1}" ]; then
- echo "sock"
- else
- echo "invalid"
- fi
- return 0
-}
-
-print_mtime() {
- local my_mtime="0"
-
- if [ -e "$1" ]; then
- my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
- fi
-
- echo "# Last modified: ${my_mtime}" >> $cpio_list
- echo "" >> $cpio_list
-}
-
-list_parse() {
- if [ -z "$dep_list" -o -L "$1" ]; then
- return
- fi
- echo "$1" | sed 's/:/\\:/g; s/$/ \\/' >> $dep_list
-}
-
-# for each file print a line in following format
-# <filetype> <name> <path to file> <octal mode> <uid> <gid>
-# for links, devices etc the format differs. See gen_init_cpio for details
-parse() {
- local location="$1"
- local name="/${location#${srcdir}}"
- # change '//' into '/'
- name=$(echo "$name" | sed -e 's://*:/:g')
- local mode="$2"
- local uid="$3"
- local gid="$4"
- local ftype=$(filetype "${location}")
- # remap uid/gid to 0 if necessary
- [ "$root_uid" = "squash" ] && uid=0 || [ "$uid" -eq "$root_uid" ] && uid=0
- [ "$root_gid" = "squash" ] && gid=0 || [ "$gid" -eq "$root_gid" ] && gid=0
- local str="${mode} ${uid} ${gid}"
-
- [ "${ftype}" = "invalid" ] && return 0
- [ "${location}" = "${srcdir}" ] && return 0
-
- case "${ftype}" in
- "file")
- str="${ftype} ${name} ${location} ${str}"
- ;;
- "nod")
- local dev="`LC_ALL=C ls -l "${location}"`"
- local maj=`field 5 ${dev}`
- local min=`field 6 ${dev}`
- maj=${maj%,}
-
- [ -b "${location}" ] && dev="b" || dev="c"
-
- str="${ftype} ${name} ${str} ${dev} ${maj} ${min}"
- ;;
- "slink")
- local target=`readlink "${location}"`
- str="${ftype} ${name} ${target} ${str}"
- ;;
- *)
- str="${ftype} ${name} ${str}"
- ;;
- esac
-
- echo "${str}" >> $cpio_list
-
- return 0
-}
-
-unknown_option() {
- printf "ERROR: unknown option \"$arg\"\n" >&2
- printf "If the filename validly begins with '-', " >&2
- printf "then it must be prefixed\n" >&2
- printf "by './' so that it won't be interpreted as an option." >&2
- printf "\n" >&2
- usage >&2
- exit 1
-}
-
-header() {
- printf "\n#####################\n# $1\n" >> $cpio_list
-}
-
-# process one directory (incl sub-directories)
-dir_filelist() {
- header "$1"
-
- srcdir=$(echo "$1" | sed -e 's://*:/:g')
- dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" | LC_ALL=C sort)
-
- # If $dirlist is only one line, then the directory is empty
- if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
- print_mtime "$1"
-
- echo "${dirlist}" | \
- while read x; do
- list_parse $x
- parse $x
- done
- fi
-}
-
-input_file() {
- source="$1"
- if [ -f "$1" ]; then
- # If a regular file is specified, assume it is in
- # gen_init_cpio format
- header "$1"
- print_mtime "$1" >> $cpio_list
- cat "$1" >> $cpio_list
- if [ -n "$dep_list" ]; then
- echo "$1 \\" >> $dep_list
- cat "$1" | while read type dir file perm ; do
- if [ "$type" = "file" ]; then
- echo "$file \\" >> $dep_list
- fi
- done
- fi
- elif [ -d "$1" ]; then
- # If a directory is specified then add all files in it to fs
- dir_filelist "$1"
- else
- echo " ${prog}: Cannot open '$1'" >&2
- exit 1
- fi
-}
-
-prog=$0
-root_uid=0
-root_gid=0
-dep_list=
-timestamp=
-cpio_list=$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)
-# gen_init_cpio writes to stdout by default
-output=""
-
-trap "rm -f $cpio_list" EXIT
-
-while [ $# -gt 0 ]; do
- arg="$1"
- shift
- case "$arg" in
- "-l") # files included in initramfs - used by kbuild
- dep_list="$1"
- echo "deps_initramfs := \\" > $dep_list
- shift
- ;;
- "-o") # generate cpio image named $1
- output="-o $1"
- shift
- ;;
- "-u") # map $1 to uid=0 (root)
- root_uid="$1"
- [ "$root_uid" = "-1" ] && root_uid=$(id -u || echo 0)
- shift
- ;;
- "-g") # map $1 to gid=0 (root)
- root_gid="$1"
- [ "$root_gid" = "-1" ] && root_gid=$(id -g || echo 0)
- shift
- ;;
- "-d") # date for file mtimes
- timestamp="$(date -d"$1" +%s || :)"
- if test -n "$timestamp"; then
- timestamp="-t $timestamp"
- fi
- shift
- ;;
- "-h")
- usage
- exit 0
- ;;
- *)
- case "$arg" in
- "-"*)
- unknown_option
- ;;
- *) # input file/dir - process it
- input_file "$arg"
- ;;
- esac
- ;;
- esac
-done
-
-# If output_file is set we will generate cpio archive
-# we are careful to delete tmp files
-usr/gen_init_cpio $output $timestamp $cpio_list
diff --git a/na/test/module.mk b/na/test/module.mk
deleted file mode 100644
index 47a693c..0000000
--- a/na/test/module.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-# test/module.mk
-SRCS_CC += test/selftest.cc
-SRCS_CC += test/test_pmm.cc
-SRCS_CC += test/test_slab.cc
-SRCS_CC += test/test_spinlock.cc
-SRCS_CC += test/test_mutex.cc
-SRCS_CC += test/test_driver_model.cc
-SRCS_CC += test/test_rbtree.cc
-SRCS_CC += test/test_vmm.cc
-SRCS_CC += test/test_vma.cc
-SRCS_CC += test/test_atomic.cc
-SRCS_CC += test/test_fdt.cc
-SRCS_CC += test/test_trap.cc
-SRCS_CC += test/test_sched.cc
-SRCS_CC += test/test_klog.cc
-SRCS_CC += test/test_random.cc
-SRCS_CC += test/test_waitqueue.cc
-SRCS_CC += test/test_uaccess.cc
-SRCS_CC += test/test_sys_mm.cc
-SRCS_CC += test/test_smp.cc
-SRCS_CC += test/test_dcache.cc
-SRCS_CC += test/test_namei.cc
-SRCS_CC += test/test_vfs_io.cc
-SRCS_CC += test/test_exec.cc
-SRCS_CC += test/test_pipe_devfs.cc
-SRCS_CC += test/test_sys_process.cc
-SRCS_CC += test/test_signal.cc
-SRCS_CC += test/test_pid.cc
-SRCS_CC += test/test_sys_fs_extra.cc
-SRCS_CC += test/test_page_cache.cc
diff --git a/na/test/selftest.cc b/na/test/selftest.cc
deleted file mode 100644
index e1bb2b4..0000000
--- a/na/test/selftest.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot Self-Test Runner
- *
- * Entry point that invokes all registered self-test suites.
- * Called from kernel_main() after all subsystem initialization.
- */
-
-#include <lume/selftest.h>
-
-/* Individual test suite entry points */
-void selftest_pmm();
-void selftest_slab();
-void selftest_spinlock();
-void selftest_mutex();
-void selftest_driver_model();
-void selftest_rbtree();
-void selftest_vmm();
-void selftest_fdt();
-void selftest_vma();
-void selftest_atomic();
-void selftest_trap();
-void selftest_sched();
-void selftest_klog();
-void selftest_random();
-void selftest_waitqueue();
-void selftest_uaccess();
-void selftest_sys_mm();
-void selftest_dcache();
-void selftest_namei();
-void selftest_vfs_io();
-void selftest_exec();
-void selftest_devfs();
-void selftest_pipe();
-void selftest_fcntl();
-void selftest_sys_process();
-void selftest_signal();
-void selftest_pid();
-void selftest_sys_fs_extra();
-void selftest_page_cache();
-
-void selftest_run_all()
-{
- kprintf("\n========================================\n");
- kprintf(" Boot Self-Test Suite\n");
- kprintf("========================================\n");
-
- selftest_spinlock();
- selftest_mutex();
- selftest_driver_model();
- selftest_pmm();
- selftest_slab();
- selftest_rbtree();
- selftest_vmm();
- selftest_vma();
- selftest_atomic();
- selftest_fdt();
- selftest_trap();
- selftest_sched();
- selftest_klog();
- selftest_random();
- selftest_waitqueue();
- selftest_uaccess();
- selftest_sys_mm();
- selftest_dcache();
- selftest_namei();
- selftest_vfs_io();
- selftest_devfs();
- selftest_pipe();
- selftest_fcntl();
- selftest_sys_process();
- selftest_signal();
- selftest_pid();
- selftest_sys_fs_extra();
- selftest_page_cache();
- selftest_exec();
-
- kprintf("========================================\n");
- kprintf(" All self-tests PASSED\n");
- kprintf("========================================\n\n");
-}
diff --git a/na/test/test_atomic.cc b/na/test/test_atomic.cc
deleted file mode 100644
index 9851e7b..0000000
--- a/na/test/test_atomic.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot Self-Test: lume::atomic<T>
- *
- * Tests:
- * 1. Default construction (zero-initialized)
- * 2. Explicit construction
- * 3. store / load round-trip with different memory orders
- * 4. exchange returns old value
- * 5. fetch_add / fetch_sub arithmetic
- * 6. compare_exchange success and failure paths
- */
-
-#include <lume/selftest.h>
-#include <lume/atomic.h>
-
-void selftest_atomic() {
- st_begin("atomic: default construction is zero");
- {
- lume::atomic<uint32> a;
- ST_ASSERT_EQ(a.load(), 0U);
- }
- st_pass();
-
- st_begin("atomic: explicit construction");
- {
- lume::atomic<uint64> a(42);
- ST_ASSERT_EQ(a.load(), 42ULL);
- }
- st_pass();
-
- st_begin("atomic: store/load round-trip");
- {
- lume::atomic<uint32> a;
- a.store(0xDEAD, __ATOMIC_RELEASE);
- ST_ASSERT_EQ(a.load(__ATOMIC_ACQUIRE), 0xDEADU);
-
- a.store(0, __ATOMIC_SEQ_CST);
- ST_ASSERT_EQ(a.load(__ATOMIC_SEQ_CST), 0U);
- }
- st_pass();
-
- st_begin("atomic: exchange returns old value");
- {
- lume::atomic<uint32> a(10);
- uint32 old = a.exchange(20);
- ST_ASSERT_EQ(old, 10U);
- ST_ASSERT_EQ(a.load(), 20U);
- }
- st_pass();
-
- st_begin("atomic: fetch_add / fetch_sub");
- {
- lume::atomic<uint32> a(100);
-
- uint32 old = a.fetch_add(5);
- ST_ASSERT_EQ(old, 100U);
- ST_ASSERT_EQ(a.load(), 105U);
-
- old = a.fetch_sub(10);
- ST_ASSERT_EQ(old, 105U);
- ST_ASSERT_EQ(a.load(), 95U);
-
- // Underflow wraps (unsigned)
- lume::atomic<uint32> b(0);
- old = b.fetch_sub(1);
- ST_ASSERT_EQ(old, 0U);
- ST_ASSERT_EQ(b.load(), 0xFFFFFFFFU);
- }
- st_pass();
-
- st_begin("atomic: compare_exchange success and failure");
- {
- lume::atomic<uint32> a(42);
-
- // Success path: expected matches, swap happens
- uint32 expected = 42;
- bool ok = a.compare_exchange(expected, 99);
- ST_ASSERT(ok);
- ST_ASSERT_EQ(a.load(), 99U);
- ST_ASSERT_EQ(expected, 42U); // expected unchanged on success
-
- // Failure path: expected doesn't match, expected gets updated
- expected = 0; // wrong
- ok = a.compare_exchange(expected, 200);
- ST_ASSERT(!ok);
- ST_ASSERT_EQ(a.load(), 99U); // value unchanged
- ST_ASSERT_EQ(expected, 99U); // expected updated to actual value
- }
- st_pass();
-
- st_begin("atomic: 64-bit fetch_add correctness");
- {
- lume::atomic<uint64> a(0xFFFFFFFF00000000ULL);
- uint64 old = a.fetch_add(1);
- ST_ASSERT_EQ(old, 0xFFFFFFFF00000000ULL);
- ST_ASSERT_EQ(a.load(), 0xFFFFFFFF00000001ULL);
- }
- st_pass();
-}
diff --git a/na/test/test_dcache.cc b/na/test/test_dcache.cc
deleted file mode 100644
index 9e0eec5..0000000
--- a/na/test/test_dcache.cc
+++ /dev/null
@@ -1,232 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_dcache.cc — Self-tests for the dentry cache layer
- */
-
-#include <lume/selftest.h>
-#include <lume/dcache.h>
-#include <lume/fs.h>
-#include <lume/mount.h>
-#include <lume/errno.h>
-#include <lume/slab.h>
-
-/* Root dentry/mount from VFS init */
-extern dentry* g_root_dentry;
-extern vfsmount* g_root_mnt;
-
-void selftest_dcache()
-{
- /* ---- d_alloc: basic allocation under root ---- */
- st_begin("dcache: d_alloc basic");
- {
- qstr name{};
- name.name = "test_child";
- name.len = 10;
- name.hash = full_name_hash(name.name, name.len);
-
- dentry* child = d_alloc(g_root_dentry, &name);
- ST_ASSERT(child != nullptr);
- ST_ASSERT_EQ(child->d_name.len, 10u);
- ST_ASSERT(child->d_parent == g_root_dentry);
- ST_ASSERT(child->d_inode == nullptr); /* not instantiated yet */
- ST_ASSERT(child->d_sb == g_root_dentry->d_sb);
-
- /* Name was copied into inline buffer */
- bool match = true;
- for (uint32 i = 0; i < name.len; i++) {
- if (child->d_name.name[i] != name.name[i]) { match = false; break; }
- }
- ST_ASSERT(match);
-
- d_drop(child);
- dput(child);
- }
- st_pass();
-
- /* ---- d_lookup: find existing child ---- */
- st_begin("dcache: d_lookup finds child");
- {
- qstr name{};
- name.name = "lookup_me";
- name.len = 9;
- name.hash = full_name_hash(name.name, name.len);
-
- dentry* child = d_alloc(g_root_dentry, &name);
- ST_ASSERT(child != nullptr);
-
- /* d_lookup should find it */
- dentry* found = d_lookup(g_root_dentry, &name);
- ST_ASSERT(found != nullptr);
- ST_ASSERT(found == child);
- dput(found); /* drop d_lookup ref */
-
- d_drop(child);
- dput(child);
- }
- st_pass();
-
- /* ---- d_lookup: miss returns nullptr ---- */
- st_begin("dcache: d_lookup miss returns nullptr");
- {
- qstr name{};
- name.name = "nonexistent_xyz";
- name.len = 15;
- name.hash = full_name_hash(name.name, name.len);
-
- dentry* found = d_lookup(g_root_dentry, &name);
- ST_ASSERT(found == nullptr);
- }
- st_pass();
-
- /* ---- d_instantiate: bind inode to dentry ---- */
- st_begin("dcache: d_instantiate binds inode");
- {
- qstr name{};
- name.name = "has_inode";
- name.len = 9;
- name.hash = full_name_hash(name.name, name.len);
-
- dentry* child = d_alloc(g_root_dentry, &name);
- ST_ASSERT(child != nullptr);
- ST_ASSERT(child->d_inode == nullptr);
-
- /* Create a dummy inode */
- inode* ip = new_inode(g_root_dentry->d_sb);
- ST_ASSERT(ip != nullptr);
- ip->i_mode = S_IFREG | 0644;
-
- d_instantiate(child, ip);
- ST_ASSERT(child->d_inode == ip);
- ST_ASSERT(child->d_flags & DCACHE_REGULAR_TYPE);
-
- d_drop(child);
- dput(child);
- }
- st_pass();
-
- /* ---- dget/dput: refcount ---- */
- st_begin("dcache: dget/dput refcount");
- {
- qstr name{};
- name.name = "reftest";
- name.len = 7;
- name.hash = full_name_hash(name.name, name.len);
-
- dentry* child = d_alloc(g_root_dentry, &name);
- ST_ASSERT(child != nullptr);
- /* d_alloc sets refcount = 1 */
- int rc1 = child->d_lockref.load();
- ST_ASSERT(rc1 >= 1);
-
- dget(child);
- int rc2 = child->d_lockref.load();
- ST_ASSERT_EQ(rc2, rc1 + 1);
-
- dput(child);
- int rc3 = child->d_lockref.load();
- ST_ASSERT_EQ(rc3, rc1);
-
- d_drop(child);
- dput(child);
- }
- st_pass();
-
- /* ---- d_drop: removes from parent children list ---- */
- st_begin("dcache: d_drop removes from parent");
- {
- qstr name{};
- name.name = "dropme";
- name.len = 6;
- name.hash = full_name_hash(name.name, name.len);
-
- dentry* child = d_alloc(g_root_dentry, &name);
- ST_ASSERT(child != nullptr);
-
- /* Should be findable */
- dentry* f1 = d_lookup(g_root_dentry, &name);
- ST_ASSERT(f1 != nullptr);
- dput(f1);
-
- /* Drop it */
- d_drop(child);
-
- /* Should no longer be findable */
- dentry* f2 = d_lookup(g_root_dentry, &name);
- ST_ASSERT(f2 == nullptr);
-
- dput(child);
- }
- st_pass();
-
- /* ---- d_path: root returns "/" ---- */
- st_begin("dcache: d_path root returns /");
- {
- path p{};
- p.de = g_root_dentry;
- p.mnt = g_root_mnt;
-
- char buf[64];
- char* result = d_path(&p, buf, sizeof(buf));
- ST_ASSERT(result != nullptr);
- ST_ASSERT(result[0] == '/');
- ST_ASSERT(result[1] == '\0');
- }
- st_pass();
-
- /* ---- d_path: child path ---- */
- st_begin("dcache: d_path child returns /name");
- {
- qstr name{};
- name.name = "pathtest";
- name.len = 8;
- name.hash = full_name_hash(name.name, name.len);
-
- dentry* child = d_alloc(g_root_dentry, &name);
- ST_ASSERT(child != nullptr);
-
- path p{};
- p.de = child;
- p.mnt = g_root_mnt;
-
- char buf[64];
- char* result = d_path(&p, buf, sizeof(buf));
- ST_ASSERT(result != nullptr);
-
- /* Should be "/pathtest" */
- const char* expected = "/pathtest";
- bool eq = true;
- for (int i = 0; expected[i]; i++) {
- if (result[i] != expected[i]) { eq = false; break; }
- }
- ST_ASSERT(eq);
-
- d_drop(child);
- dput(child);
- }
- st_pass();
-
- /* ---- d_alloc_anon: root-like dentry ---- */
- st_begin("dcache: d_alloc_anon");
- {
- dentry* anon = d_alloc_anon(g_root_dentry->d_sb);
- ST_ASSERT(anon != nullptr);
- ST_ASSERT(anon->d_parent == anon); /* root: parent is self */
- ST_ASSERT(anon->d_sb == g_root_dentry->d_sb);
- dput(anon);
- }
- st_pass();
-
- /* ---- full_name_hash: deterministic ---- */
- st_begin("dcache: full_name_hash deterministic");
- {
- uint32 h1 = full_name_hash("hello", 5);
- uint32 h2 = full_name_hash("hello", 5);
- ST_ASSERT_EQ(h1, h2);
-
- uint32 h3 = full_name_hash("world", 5);
- ST_ASSERT_NE(h1, h3);
- }
- st_pass();
-}
diff --git a/na/test/test_driver_model.cc b/na/test/test_driver_model.cc
deleted file mode 100644
index 8f105b3..0000000
--- a/na/test/test_driver_model.cc
+++ /dev/null
@@ -1,209 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_driver_model — exercises kref, kobject, and the bus/device/driver
- * core. Uses a dummy bus + dummy driver + dummy device so the test is
- * self-contained and doesn't depend on FDT/platform_device.
- */
-
-#include <lume/selftest.h>
-#include <lume/kref.h>
-#include <lume/kobject.h>
-#include <lume/device.h>
-
-/* ------------------------------------------------------------------ */
-/* Test 1: kref get/put + release fires exactly once */
-/* ------------------------------------------------------------------ */
-
-namespace {
-
-struct krefable {
- kref ref;
- int release_count;
-};
-
-void krefable_release(kref* r)
-{
- auto* k = container_of(r, krefable, ref);
- k->release_count++;
-}
-
-void test_kref()
-{
- st_begin("kref: init/get/put");
-
- krefable k = {};
- kref_init(&k.ref);
-
- ST_ASSERT_EQ(kref_read(&k.ref), 1);
-
- kref_get(&k.ref);
- kref_get(&k.ref);
- ST_ASSERT_EQ(kref_read(&k.ref), 3);
-
- ST_ASSERT_EQ(kref_put(&k.ref, krefable_release), 0);
- ST_ASSERT_EQ(kref_put(&k.ref, krefable_release), 0);
- ST_ASSERT_EQ(k.release_count, 0);
-
- ST_ASSERT_EQ(kref_put(&k.ref, krefable_release), 1);
- ST_ASSERT_EQ(k.release_count, 1);
-
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* Test 2: kobject parent/child + release on final put */
-/* ------------------------------------------------------------------ */
-
-int g_child_released = 0;
-int g_parent_released = 0;
-
-void child_release(kobject*) { g_child_released++; }
-void parent_release(kobject*) { g_parent_released++; }
-
-const kobj_type child_ktype = { .release = child_release };
-const kobj_type parent_ktype = { .release = parent_release };
-
-void test_kobject_hierarchy()
-{
- st_begin("kobject: parent/child + ref counting");
-
- g_child_released = 0;
- g_parent_released = 0;
-
- kobject parent;
- kobject_init(&parent, &parent_ktype);
-
- kobject child;
- kobject_init(&child, &child_ktype);
- kobject_add(&child, &parent, "child");
-
- /* Adding child took a ref on parent. */
- ST_ASSERT_EQ(kref_read(&parent.refcount), 2);
- ST_ASSERT(child.parent == &parent);
-
- /* Drop child's "added" reference — release should NOT fire because
- * we (the test) still hold the original kobject_init ref. */
- kobject_del(&child);
- ST_ASSERT(child.parent == nullptr);
- ST_ASSERT_EQ(kref_read(&parent.refcount), 1);
- ST_ASSERT_EQ(g_child_released, 0);
- ST_ASSERT_EQ(g_parent_released, 0);
-
- /* Drop final refs. */
- kobject_put(&child);
- ST_ASSERT_EQ(g_child_released, 1);
-
- kobject_put(&parent);
- ST_ASSERT_EQ(g_parent_released, 1);
-
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* Test 3: bus / device / driver bind via match + probe */
-/* ------------------------------------------------------------------ */
-
-struct dummy_dev { device dev; int probed; };
-struct dummy_drv { device_driver drv; const char* want; };
-
-int g_probes = 0;
-
-int dummy_match(device* dev, device_driver* drv)
-{
- auto* dd = container_of(drv, dummy_drv, drv);
- return (dev->init_name != nullptr &&
- dd->want != nullptr &&
- __builtin_strcmp(dev->init_name, dd->want) == 0) ? 1 : 0;
-}
-
-int dummy_probe(device* dev)
-{
- auto* dd = container_of(dev, dummy_dev, dev);
- dd->probed++;
- g_probes++;
- return 0;
-}
-
-bus_type dummy_bus = {
- .name = "test_dummy",
- .match = dummy_match,
- .probe = nullptr, /* fall through to drv->probe */
- .devices = {},
- .drivers = {},
- .lock = {},
- .registered = false,
-};
-
-void test_bus_bind()
-{
- st_begin("driver model: bus / driver / device bind");
-
- g_probes = 0;
- ST_ASSERT_EQ(bus_register(&dummy_bus), 0);
-
- /* Register driver before any matching device exists. */
- dummy_drv drv_a = {};
- drv_a.drv.name = "drv_a";
- drv_a.drv.bus = &dummy_bus;
- drv_a.drv.probe = dummy_probe;
- drv_a.want = "compat_a";
- ST_ASSERT_EQ(driver_register(&drv_a.drv), 0);
-
- /* Register a device that should bind to drv_a immediately. */
- dummy_dev dev_a = {};
- dev_a.dev.bus = &dummy_bus;
- dev_a.dev.init_name = "compat_a";
- ST_ASSERT_EQ(device_register(&dev_a.dev), 0);
- ST_ASSERT_EQ(dev_a.probed, 1);
- ST_ASSERT(dev_a.dev.driver == &drv_a.drv);
-
- /* Register a device first, then a driver — should also bind. */
- dummy_dev dev_b = {};
- dev_b.dev.bus = &dummy_bus;
- dev_b.dev.init_name = "compat_b";
- ST_ASSERT_EQ(device_register(&dev_b.dev), 0);
- ST_ASSERT_EQ(dev_b.probed, 0);
-
- dummy_drv drv_b = {};
- drv_b.drv.name = "drv_b";
- drv_b.drv.bus = &dummy_bus;
- drv_b.drv.probe = dummy_probe;
- drv_b.want = "compat_b";
- ST_ASSERT_EQ(driver_register(&drv_b.drv), 0);
- ST_ASSERT_EQ(dev_b.probed, 1);
- ST_ASSERT(dev_b.dev.driver == &drv_b.drv);
-
- /* Non-matching device sits unbound. */
- dummy_dev dev_c = {};
- dev_c.dev.bus = &dummy_bus;
- dev_c.dev.init_name = "compat_unknown";
- ST_ASSERT_EQ(device_register(&dev_c.dev), 0);
- ST_ASSERT_EQ(dev_c.probed, 0);
- ST_ASSERT(dev_c.dev.driver == nullptr);
-
- ST_ASSERT_EQ(g_probes, 2);
-
- /* Cleanup. */
- device_unregister(&dev_a.dev);
- device_unregister(&dev_b.dev);
- device_unregister(&dev_c.dev);
- driver_unregister(&drv_a.drv);
- driver_unregister(&drv_b.drv);
-
- st_pass();
-}
-
-} // namespace
-
-/* ------------------------------------------------------------------ */
-/* Entry */
-/* ------------------------------------------------------------------ */
-
-void selftest_driver_model()
-{
- test_kref();
- test_kobject_hierarchy();
- test_bus_bind();
-}
diff --git a/na/test/test_exec.cc b/na/test/test_exec.cc
deleted file mode 100644
index bbfa3b6..0000000
--- a/na/test/test_exec.cc
+++ /dev/null
@@ -1,447 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_exec.cc — Self-tests for execve / ELF loader
- *
- * Tests the ELF validation, binary loading, and user stack construction
- * by creating synthetic ELF binaries in RamFS and invoking do_execve
- * in a controlled environment.
- *
- * We cannot test the actual user-space transition (that requires
- * sret to U-mode), but we CAN test everything up to and including
- * the trapframe setup.
- */
-
-#include <lume/selftest.h>
-#include <lume/elf.h>
-#include <lume/task.h>
-#include <lume/vmm.h>
-#include <lume/pmm.h>
-#include <lume/addr.h>
-#include <lume/errno.h>
-#include <lume/fdtable.h>
-#include <lume/fs.h>
-#include <lume/fs_struct.h>
-#include <lume/dcache.h>
-#include <lume/namei.h>
-#include <lume/file.h>
-#include <lume/mm_layout.h>
-#include <arch/trapframe.h>
-#include <arch/pmap.h>
-
-/* Extern: do_execve (kernel/exec.cc) */
-int do_execve(const char* pathname, const char* const* argv,
- const char* const* envp);
-
-/* Extern: VFS file creation syscalls */
-int64 sys_openat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_write(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_close(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* ================================================================== */
-/* Helper: build a minimal valid RISC-V ELF64 in a buffer */
-/* ================================================================== */
-
-struct TestElf {
- Elf64_Ehdr ehdr;
- Elf64_Phdr phdr;
- uint8 code[16]; /* Tiny "program" — just a few NOPs */
-};
-
-/*
- * Build a minimal static ELF64 for RISC-V.
- * Single PT_LOAD segment mapping the tiny code at 0x10000.
- * Entry point = 0x10000 + offsetof(code).
- */
-static void build_minimal_elf(TestElf* elf)
-{
- /* Zero everything */
- for (uint64 i = 0; i < sizeof(*elf); i++)
- reinterpret_cast<uint8*>(elf)[i] = 0;
-
- /* ELF header */
- Elf64_Ehdr* e = &elf->ehdr;
- e->e_ident[EI_MAG0] = ELFMAG0;
- e->e_ident[EI_MAG1] = ELFMAG1;
- e->e_ident[EI_MAG2] = ELFMAG2;
- e->e_ident[EI_MAG3] = ELFMAG3;
- e->e_ident[EI_CLASS] = ELFCLASS64;
- e->e_ident[EI_DATA] = ELFDATA2LSB;
- e->e_ident[EI_VERSION] = 1;
- e->e_type = ET_EXEC;
- e->e_machine = EM_RISCV;
- e->e_version = 1;
- e->e_phoff = sizeof(Elf64_Ehdr);
- e->e_phentsize = sizeof(Elf64_Phdr);
- e->e_phnum = 1;
- e->e_ehsize = sizeof(Elf64_Ehdr);
-
- /* Code section offset within the file */
- uint64 code_offset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr);
-
- /* Entry point = vaddr of code */
- uint64 load_vaddr = 0x10000;
- e->e_entry = load_vaddr + code_offset;
-
- /* Program header — single PT_LOAD covering entire file */
- Elf64_Phdr* p = &elf->phdr;
- p->p_type = PT_LOAD;
- p->p_flags = PF_R | PF_X;
- p->p_offset = 0;
- p->p_vaddr = load_vaddr;
- p->p_paddr = load_vaddr;
- p->p_filesz = sizeof(TestElf);
- p->p_memsz = sizeof(TestElf);
- p->p_align = 0x1000;
-
- /* Code: RISC-V "unimp" (all zeros = illegal instruction, but good enough for testing) */
- /* We just need the loader to succeed; we won't actually execute this. */
- elf->code[0] = 0x13; /* nop = addi x0, x0, 0 */
- elf->code[4] = 0x13;
- elf->code[8] = 0x73; /* ebreak */
-}
-
-/* ================================================================== */
-/* Helper: write a buffer to a file in RamFS */
-/* ================================================================== */
-
-static int create_test_file(const char* path, const void* data, uint64 size)
-{
- /* Open with O_CREAT | O_WRONLY | O_TRUNC */
- int64 fd = sys_openat(
- static_cast<uint64>(-100), /* AT_FDCWD */
- reinterpret_cast<uint64>(path),
- O_CREAT | O_WRONLY | O_TRUNC,
- 0755, 0, 0);
- if (fd < 0) return static_cast<int>(fd);
-
- int64 written = sys_write(
- static_cast<uint64>(fd),
- reinterpret_cast<uint64>(data),
- size, 0, 0, 0);
-
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
-
- if (written < 0) return static_cast<int>(written);
- if (static_cast<uint64>(written) != size) return -EIO;
- return 0;
-}
-
-/* ================================================================== */
-/* Helper: ensure current task has VFS context for file ops */
-/* ================================================================== */
-
-struct ExecTestCtx {
- TaskControlBlock* curr;
- VmSpace* old_mm;
- arch::TrapFrame fake_tf;
- arch::TrapFrame* old_tf;
- uint64 saved_satp;
-};
-
-static void exec_test_setup(ExecTestCtx* ctx)
-{
- ctx->curr = current_task();
-
- /* Save kernel SATP so we can restore it in teardown */
- asm volatile("csrr %0, satp" : "=r"(ctx->saved_satp));
-
- /* Save and replace mm with a fresh one */
- ctx->old_mm = ctx->curr->mm;
- ctx->curr->mm = VmSpace::create();
-
- /* Set up a fake trapframe so do_execve can modify it */
- for (uint64 i = 0; i < sizeof(ctx->fake_tf); i++)
- reinterpret_cast<uint8*>(&ctx->fake_tf)[i] = 0;
- ctx->old_tf = ctx->curr->trapframe;
- ctx->curr->trapframe = &ctx->fake_tf;
-}
-
-static void exec_test_teardown(ExecTestCtx* ctx)
-{
- /* Restore trapframe */
- ctx->curr->trapframe = ctx->old_tf;
-
- /* Destroy the test mm (may have been replaced by do_execve) */
- VmSpace* test_mm = ctx->curr->mm;
- ctx->curr->mm = ctx->old_mm;
- if (test_mm && test_mm != ctx->old_mm) {
- /* If do_execve succeeded, it activated new_mm's page table.
- * Switch back to the kernel page table before destroying. */
- asm volatile("csrw satp, %0" :: "r"(ctx->saved_satp));
- asm volatile("sfence.vma");
- test_mm->put();
- }
-}
-
-/* ================================================================== */
-/* Test cases */
-/* ================================================================== */
-
-void selftest_exec()
-{
- /* ---- 1a. Non-existent path under missing directory ---- */
- st_begin("exec: missing path returns error");
- {
- const char* argv[] = { "/no_such_dir/no_file", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/no_such_dir/no_file", argv, envp);
- ST_ASSERT(ret < 0); /* -ENOENT from filename_lookup */
- }
- st_pass();
-
- /* ---- 1b. Directory is not executable ---- */
- st_begin("exec: directory returns -EACCES");
- {
- /* "/" exists and is a directory */
- const char* argv[] = { "/dev", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/dev", argv, envp);
- ST_ASSERT_EQ(ret, -EACCES);
- }
- st_pass();
-
- /* ---- 2. Bad ELF magic ---- */
- st_begin("exec: bad ELF magic returns -ENOEXEC");
- {
- uint8 garbage[128];
- for (int i = 0; i < 128; i++) garbage[i] = 0xAA;
- int err = create_test_file("/test_bad_magic", garbage, 128);
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- const char* argv[] = { "/test_bad_magic", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/test_bad_magic", argv, envp);
- ST_ASSERT_EQ(ret, -ENOEXEC);
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-
- /* ---- 3. Wrong ELF class (32-bit) ---- */
- st_begin("exec: 32-bit ELF returns -ENOEXEC");
- {
- TestElf elf;
- build_minimal_elf(&elf);
- elf.ehdr.e_ident[EI_CLASS] = 1; /* ELFCLASS32 */
- int err = create_test_file("/test_elf32", &elf, sizeof(elf));
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- const char* argv[] = { "/test_elf32", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/test_elf32", argv, envp);
- ST_ASSERT_EQ(ret, -ENOEXEC);
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-
- /* ---- 4. Wrong endianness (big-endian) ---- */
- st_begin("exec: big-endian ELF returns -ENOEXEC");
- {
- TestElf elf;
- build_minimal_elf(&elf);
- elf.ehdr.e_ident[EI_DATA] = 2; /* ELFDATA2MSB */
- int err = create_test_file("/test_be", &elf, sizeof(elf));
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- const char* argv[] = { "/test_be", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/test_be", argv, envp);
- ST_ASSERT_EQ(ret, -ENOEXEC);
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-
- /* ---- 5. Wrong machine (x86_64 instead of RISC-V) ---- */
- st_begin("exec: wrong machine type returns -ENOEXEC");
- {
- TestElf elf;
- build_minimal_elf(&elf);
- elf.ehdr.e_machine = 62; /* EM_X86_64 */
- int err = create_test_file("/test_x86", &elf, sizeof(elf));
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- const char* argv[] = { "/test_x86", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/test_x86", argv, envp);
- ST_ASSERT_EQ(ret, -ENOEXEC);
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-
- /* ---- 6. Truncated ELF (too small for header) ---- */
- st_begin("exec: truncated ELF returns -ENOEXEC");
- {
- /* Valid magic but file is too short for full header */
- uint8 tiny[8] = { 0x7f, 'E', 'L', 'F', 2, 1, 1, 0 };
- int err = create_test_file("/test_tiny", tiny, sizeof(tiny));
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- const char* argv[] = { "/test_tiny", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/test_tiny", argv, envp);
- ST_ASSERT_EQ(ret, -ENOEXEC);
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-
- /* ---- 7. Zero program headers ---- */
- st_begin("exec: zero phnum returns -ENOEXEC");
- {
- TestElf elf;
- build_minimal_elf(&elf);
- elf.ehdr.e_phnum = 0;
- int err = create_test_file("/test_nophdr", &elf, sizeof(elf));
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- const char* argv[] = { "/test_nophdr", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/test_nophdr", argv, envp);
- ST_ASSERT_EQ(ret, -ENOEXEC);
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-
- /* ---- 8. Valid ELF: successful load and trapframe setup ---- */
- st_begin("exec: valid ELF loads and sets up trapframe");
- {
- TestElf elf;
- build_minimal_elf(&elf);
- int err = create_test_file("/test_valid", &elf, sizeof(elf));
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- const char* argv[] = { "/test_valid", nullptr };
- const char* envp[] = { "HOME=/", nullptr };
- int ret = do_execve("/test_valid", argv, envp);
- ST_ASSERT_EQ(ret, 0);
-
- /* Verify trapframe was set up correctly */
- arch::TrapFrame* tf = &ctx.fake_tf;
-
- /* Entry point should match what we set in the ELF */
- uint64 code_offset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr);
- uint64 expected_entry = 0x10000 + code_offset;
- ST_ASSERT_EQ(tf->epc, expected_entry);
-
- /* Stack pointer should be in user space, below the stack top */
- ST_ASSERT(tf->sp > 0);
- ST_ASSERT(tf->sp < kUserStackTop);
- ST_ASSERT(tf->sp >= kUserStackBase);
-
- /* sstatus: SPP=0 (U-mode), SPIE=1 (interrupts enabled on sret) */
- uint64 spp_bit = (1ULL << 8);
- uint64 spie_bit = (1ULL << 5);
- ST_ASSERT_EQ(tf->sstatus & spp_bit, 0ULL); /* Not S-mode */
- ST_ASSERT_EQ(tf->sstatus & spie_bit, spie_bit); /* SPIE set */
-
- /* General registers should be zeroed */
- ST_ASSERT_EQ(tf->ra, 0ULL);
- ST_ASSERT_EQ(tf->gp, 0ULL);
- ST_ASSERT_EQ(tf->tp, 0ULL);
- for (int i = 0; i < 7; i++)
- ST_ASSERT_EQ(tf->t[i], 0ULL);
- for (int i = 0; i < 12; i++)
- ST_ASSERT_EQ(tf->s[i], 0ULL);
- for (int i = 0; i < 8; i++)
- ST_ASSERT_EQ(tf->a[i], 0ULL);
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-
- /* ---- 9. Verify user stack layout (argc/argv) ---- */
- st_begin("exec: user stack has correct argc/argv layout");
- {
- TestElf elf;
- build_minimal_elf(&elf);
- int err = create_test_file("/test_stack", &elf, sizeof(elf));
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- const char* argv[] = { "/test_stack", "arg1", nullptr };
- const char* envp[] = { "A=B", nullptr };
- int ret = do_execve("/test_stack", argv, envp);
- ST_ASSERT_EQ(ret, 0);
-
- /* The stack was set up in the new mm.
- * Read argc from the top of the stack using pmap lookup. */
- VmSpace* mm = ctx.curr->mm;
- uint64 sp = ctx.fake_tf.sp;
-
- /* Look up the physical page for sp */
- uint64 pa = 0;
- bool mapped = pmap::lookup(mm->root_pa, sp & ~(kPageSize - 1), &pa);
- ST_ASSERT(mapped);
-
- /* Read argc (first uint64 on stack) */
- uint64 page_offset = sp & (kPageSize - 1);
- uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
- uint64 argc = *reinterpret_cast<uint64*>(kva + page_offset);
- ST_ASSERT_EQ(argc, 2ULL); /* "/test_stack" and "arg1" */
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-
- /* ---- 10. Multiple consecutive execve replaces mm correctly ---- */
- st_begin("exec: consecutive execve replaces address space");
- {
- TestElf elf;
- build_minimal_elf(&elf);
- int err = create_test_file("/test_reexec", &elf, sizeof(elf));
- ST_ASSERT_EQ(err, 0);
-
- ExecTestCtx ctx;
- exec_test_setup(&ctx);
-
- /* First exec */
- const char* argv[] = { "/test_reexec", nullptr };
- const char* envp[] = { nullptr };
- int ret = do_execve("/test_reexec", argv, envp);
- ST_ASSERT_EQ(ret, 0);
-
- VmSpace* first_mm = ctx.curr->mm;
- ST_ASSERT(first_mm != nullptr);
-
- /* Second exec — should replace the first mm */
- ret = do_execve("/test_reexec", argv, envp);
- ST_ASSERT_EQ(ret, 0);
-
- /* mm should be different (new address space) */
- VmSpace* second_mm = ctx.curr->mm;
- ST_ASSERT(second_mm != nullptr);
- ST_ASSERT_NE(first_mm, second_mm);
-
- exec_test_teardown(&ctx);
- }
- st_pass();
-}
diff --git a/na/test/test_fdt.cc b/na/test/test_fdt.cc
deleted file mode 100644
index cf09029..0000000
--- a/na/test/test_fdt.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot Self-Test: FDT (Flattened Device Tree)
- */
-
-#include <lume/selftest.h>
-#include <lume/fdt.h>
-
-// Manually declare what we need to avoid header conflicts with toolchain libc
-extern "C" int strcmp(const char *s1, const char *s2);
-
-void selftest_fdt() {
- st_begin("fdt: tree root access");
- {
- DeviceNode* root = fdt_get_node_by_path("/");
- ST_ASSERT(root != nullptr);
- ST_ASSERT_EQ(strcmp(root->get_name(), ""), 0);
- }
- st_pass();
-
- st_begin("fdt: find existing compatible node (UART)");
- {
- // QEMU virt RISC-V platform always has a ns16550a UART
- DeviceNode* uart = fdt_find_compatible("ns16550a");
- ST_ASSERT(uart != nullptr);
-
- // Use get_prop_string which is convenience helper
- const char* compat = uart->get_prop_string("compatible");
- ST_ASSERT(compat != nullptr);
-
- // Check raw property access
- const FdtProperty* reg = uart->get_prop("reg");
- ST_ASSERT(reg != nullptr);
- ST_ASSERT(reg->value != nullptr);
- ST_ASSERT(reg->len > 0);
- }
- st_pass();
-
- st_begin("fdt: non-existent node/compatible queries");
- {
- DeviceNode* missing1 = fdt_get_node_by_path("/definitely_does_not_exist_node_12345");
- ST_ASSERT(missing1 == nullptr);
-
- DeviceNode* missing2 = fdt_find_compatible("invalid_compatible_string_xyz");
- ST_ASSERT(missing2 == nullptr);
- }
- st_pass();
-}
diff --git a/na/test/test_klog.cc b/na/test/test_klog.cc
deleted file mode 100644
index 896e7ed..0000000
--- a/na/test/test_klog.cc
+++ /dev/null
@@ -1,175 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot Self-Test: klog
- *
- * Tests:
- * 1. read vs read_consume semantics
- * 2. subsystem mask filtering
- * 3. ratelimit bookkeeping
- * 4. flush advances console tail when console filtering is active
- * 5. seq counter increments on writes
- */
-
-#include <lume/selftest.h>
-#include <lume/klog.h>
-#include <string.h>
-
-static void drain_klog()
-{
- static char buf[512];
- while (true) {
- int n = klog_read_consume(buf, sizeof(buf));
- if (n <= 0) {
- break;
- }
- }
-}
-
-static bool test_read_peek_and_consume()
-{
- drain_klog();
-
- klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_test_a");
- klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_test_b");
-
- KlogStats s0;
- klog_stats(&s0);
-
- char buf[1024];
- int n = klog_read(buf, sizeof(buf) - 1);
- if (n <= 0) {
- return false;
- }
- buf[n] = '\0';
- if (!strstr(buf, "klog_test_a") || !strstr(buf, "klog_test_b")) {
- return false;
- }
-
- KlogStats s1;
- klog_stats(&s1);
- if (s1.tail != s0.tail) {
- return false;
- }
-
- int n2 = klog_read_consume(buf, sizeof(buf) - 1);
- if (n2 <= 0) {
- return false;
- }
- buf[n2] = '\0';
- if (!strstr(buf, "klog_test_a") || !strstr(buf, "klog_test_b")) {
- return false;
- }
-
- KlogStats s2;
- klog_stats(&s2);
- return s2.tail != s0.tail;
-}
-
-static bool test_subsys_mask()
-{
- drain_klog();
-
- const uint64 all_mask = (1ULL << KLOG_SUBSYS_MAX) - 1;
- klog_set_subsys_mask(1ULL << KLOG_SUBSYS_TEST);
-
- klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_subsys_ok");
- klog_ex(KLOG_INFO, KLOG_SUBSYS_GENERIC, "klog_subsys_drop");
-
- char buf[1024];
- int n = klog_read_consume(buf, sizeof(buf) - 1);
- klog_set_subsys_mask(all_mask);
-
- if (n <= 0) {
- return false;
- }
- buf[n] = '\0';
- return strstr(buf, "klog_subsys_ok") && !strstr(buf, "klog_subsys_drop");
-}
-
-static bool test_ratelimit()
-{
- KlogRateLimit rl = {};
- rl.interval = 100;
- rl.burst = 2;
-
- bool a = klog_ratelimit(&rl, 10);
- bool b = klog_ratelimit(&rl, 20);
- bool c = klog_ratelimit(&rl, 30);
-
- if (!a || !b || c) {
- return false;
- }
- if (rl.printed != 2 || rl.suppressed != 1) {
- return false;
- }
-
- bool d = klog_ratelimit(&rl, 200);
- if (!d) {
- return false;
- }
- return rl.printed == 1 && rl.suppressed == 0;
-}
-
-static bool test_flush_advances_console_tail()
-{
- drain_klog();
-
- klog_set_level(KLOG_ERR);
- klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_flush_msg");
-
- KlogStats s1;
- klog_stats(&s1);
- if (s1.console_tail == s1.head) {
- klog_set_level(static_cast<KlogLevel>(KLOG_DEFAULT_LEVEL));
- return false;
- }
-
- klog_flush();
-
- KlogStats s2;
- klog_stats(&s2);
-
- klog_set_level(static_cast<KlogLevel>(KLOG_DEFAULT_LEVEL));
- return s2.console_tail == s2.head;
-}
-
-static bool test_seq_increments()
-{
- drain_klog();
-
- KlogStats s0;
- klog_stats(&s0);
-
- klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_seq_1");
- klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_seq_2");
-
- KlogStats s1;
- klog_stats(&s1);
-
- return s1.seq >= s0.seq + 2;
-}
-
-void selftest_klog()
-{
- st_begin("klog: read vs read_consume");
- ST_ASSERT(test_read_peek_and_consume());
- st_pass();
-
- st_begin("klog: subsys mask filters");
- ST_ASSERT(test_subsys_mask());
- st_pass();
-
- st_begin("klog: ratelimit bookkeeping");
- ST_ASSERT(test_ratelimit());
- st_pass();
-
- st_begin("klog: flush advances console tail");
- ST_ASSERT(test_flush_advances_console_tail());
- st_pass();
-
- st_begin("klog: seq increments on write");
- ST_ASSERT(test_seq_increments());
- st_pass();
-}
diff --git a/na/test/test_mutex.cc b/na/test/test_mutex.cc
deleted file mode 100644
index e86c984..0000000
--- a/na/test/test_mutex.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_mutex — self-tests for the Mutex sleeping lock.
- *
- * Coverage:
- * - basic lock/unlock single-thread
- * - try_lock when free and when held
- * - mutual exclusion across kthreads (no lost increments)
- * - FIFO wake order (A acquires, B/C queue; release lets B in then C)
- */
-
-#include <lume/selftest.h>
-#include <lume/mutex.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-#include <lume/kprintf.h>
-
-extern TaskControlBlock* kthread_create(const char* name,
- void (*entry)(void*), void* arg,
- uint8 priority);
-extern void task_exit(int code);
-
-/* ------------------------------------------------------------------ */
-/* Test 1: basic lock/unlock */
-/* ------------------------------------------------------------------ */
-
-static void test_basic()
-{
- st_begin("mutex: basic lock/unlock");
-
- Mutex m;
- m.init("test_basic");
-
- ST_ASSERT(!m.is_locked());
- m.lock();
- ST_ASSERT(m.is_locked());
- ST_ASSERT(m.is_owned_by_current());
- m.unlock();
- ST_ASSERT(!m.is_locked());
-
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* Test 2: try_lock */
-/* ------------------------------------------------------------------ */
-
-static void test_trylock()
-{
- st_begin("mutex: try_lock");
-
- Mutex m;
- m.init("test_trylock");
-
- ST_ASSERT(m.try_lock() == true);
- ST_ASSERT(m.is_locked());
-
- /* A second try_lock from the same task: since owner == current,
- * we still return false (try_lock is non-recursive). */
- ST_ASSERT(m.try_lock() == false);
-
- m.unlock();
- ST_ASSERT(!m.is_locked());
- ST_ASSERT(m.try_lock() == true);
- m.unlock();
-
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* Test 3: mutual exclusion across kthreads */
-/* ------------------------------------------------------------------ */
-
-static Mutex g_mut;
-static volatile int32 g_counter = 0;
-static volatile int32 g_done = 0;
-static constexpr int32 kIters = 200;
-
-static void mutex_worker(void*)
-{
- for (int i = 0; i < kIters; i++) {
- g_mut.lock();
- int32 v = g_counter;
- /* Yield inside the critical section to expose missing mutex.
- * If the mutex were broken we'd see lost increments. */
- sched_yield();
- g_counter = v + 1;
- g_mut.unlock();
- }
- __sync_fetch_and_add(&g_done, 1);
- task_exit(0);
-}
-
-static void test_mutual_exclusion()
-{
- st_begin("mutex: mutual exclusion (2 workers)");
-
- g_mut.init("test_mutex_mx");
- g_counter = 0;
- g_done = 0;
-
- kthread_create("mut_w1", mutex_worker, nullptr, 0);
- kthread_create("mut_w2", mutex_worker, nullptr, 0);
-
- /* Spin-yield until both workers finish. */
- while (g_done < 2) {
- sched_yield();
- }
-
- ST_ASSERT_EQ(g_counter, 2 * kIters);
- ST_ASSERT(!g_mut.is_locked());
-
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* Test 4: FIFO wake order */
-/* ------------------------------------------------------------------ */
-
-static Mutex g_fifo_mut;
-static volatile int32 g_fifo_seq = 0; // monotonically increases
-static volatile int32 g_fifo_order[3]; // order each worker observed
-static volatile int32 g_fifo_done = 0;
-
-static void fifo_worker(void* arg)
-{
- int slot = static_cast<int>(reinterpret_cast<uint64>(arg));
- g_fifo_mut.lock();
- g_fifo_order[slot] = __sync_fetch_and_add(&g_fifo_seq, 1);
- g_fifo_mut.unlock();
- __sync_fetch_and_add(&g_fifo_done, 1);
- task_exit(0);
-}
-
-static void test_fifo_order()
-{
- st_begin("mutex: FIFO wake order (3 waiters)");
-
- g_fifo_mut.init("test_mutex_fifo");
- g_fifo_seq = 0;
- g_fifo_done = 0;
- g_fifo_order[0] = g_fifo_order[1] = g_fifo_order[2] = -1;
-
- /* Main grabs the lock first so workers will queue up behind. */
- g_fifo_mut.lock();
-
- /* Spawn workers in deterministic order. After each create() yield
- * once so the new thread reaches mutex.lock() and enqueues itself
- * before we spawn the next one. */
- kthread_create("fifo_A", fifo_worker, reinterpret_cast<void*>(0), 0);
- sched_yield();
- kthread_create("fifo_B", fifo_worker, reinterpret_cast<void*>(1), 0);
- sched_yield();
- kthread_create("fifo_C", fifo_worker, reinterpret_cast<void*>(2), 0);
- sched_yield();
-
- /* Release: A should fire next, then B, then C. */
- g_fifo_mut.unlock();
-
- while (g_fifo_done < 3) {
- sched_yield();
- }
-
- ST_ASSERT_EQ(g_fifo_order[0], 0);
- ST_ASSERT_EQ(g_fifo_order[1], 1);
- ST_ASSERT_EQ(g_fifo_order[2], 2);
-
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* Entry */
-/* ------------------------------------------------------------------ */
-
-void selftest_mutex()
-{
- test_basic();
- test_trylock();
- test_mutual_exclusion();
- test_fifo_order();
-}
diff --git a/na/test/test_namei.cc b/na/test/test_namei.cc
deleted file mode 100644
index 8e4ecb0..0000000
--- a/na/test/test_namei.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_namei.cc — Self-tests for pathname resolution (namei.cc)
- */
-
-#include <lume/selftest.h>
-#include <lume/namei.h>
-#include <lume/dcache.h>
-#include <lume/fs.h>
-#include <lume/file.h>
-#include <lume/mount.h>
-#include <lume/task.h>
-#include <lume/errno.h>
-
-extern dentry* g_root_dentry;
-extern vfsmount* g_root_mnt;
-
-/* sys_mkdirat from kernel/sys_fs.cc */
-int64 sys_mkdirat(uint64, uint64, uint64, uint64, uint64, uint64);
-
-void selftest_namei()
-{
- /* ---- lookup root "/" ---- */
- st_begin("namei: lookup root /");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/", 0, &p);
- ST_ASSERT_EQ(err, 0);
- ST_ASSERT(p.de == g_root_dentry);
- ST_ASSERT(p.de->d_inode != nullptr);
- ST_ASSERT(S_ISDIR(p.de->d_inode->i_mode));
- }
- st_pass();
-
- /* ---- lookup /dev (exists from boot) ---- */
- st_begin("namei: lookup /dev");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/dev", 0, &p);
- ST_ASSERT_EQ(err, 0);
- ST_ASSERT(p.de != nullptr);
- ST_ASSERT(p.de->d_inode != nullptr);
- ST_ASSERT(S_ISDIR(p.de->d_inode->i_mode));
- }
- st_pass();
-
- /* ---- lookup nonexistent path returns -ENOENT ---- */
- st_begin("namei: lookup nonexistent returns -ENOENT");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/this_does_not_exist", 0, &p);
- ST_ASSERT_EQ(err, -ENOENT);
- }
- st_pass();
-
- /* ---- lookup /dev/console (chardev) ---- */
- st_begin("namei: lookup /dev/console");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/dev/console", 0, &p);
- ST_ASSERT_EQ(err, 0);
- ST_ASSERT(p.de->d_inode != nullptr);
- ST_ASSERT(S_ISCHR(p.de->d_inode->i_mode));
- }
- st_pass();
-
- /* ---- lookup with LOOKUP_DIRECTORY on non-dir returns -ENOTDIR ---- */
- st_begin("namei: LOOKUP_DIRECTORY on file returns -ENOTDIR");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/dev/console", LOOKUP_DIRECTORY, &p);
- ST_ASSERT_EQ(err, -ENOTDIR);
- }
- st_pass();
-
- /* ---- lookup with LOOKUP_DIRECTORY on dir succeeds ---- */
- st_begin("namei: LOOKUP_DIRECTORY on dir succeeds");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/dev", LOOKUP_DIRECTORY, &p);
- ST_ASSERT_EQ(err, 0);
- ST_ASSERT(S_ISDIR(p.de->d_inode->i_mode));
- }
- st_pass();
-
- /* ---- kern_path shortcut ---- */
- st_begin("namei: kern_path works like filename_lookup");
- {
- path p{};
- int err = kern_path("/dev", 0, &p);
- ST_ASSERT_EQ(err, 0);
- ST_ASSERT(p.de->d_inode != nullptr);
- }
- st_pass();
-
- /* ---- dot path: /dev/. resolves to /dev ---- */
- st_begin("namei: /dev/. resolves to /dev");
- {
- path p1{}, p2{};
- int e1 = filename_lookup(AT_FDCWD, "/dev", 0, &p1);
- int e2 = filename_lookup(AT_FDCWD, "/dev/.", 0, &p2);
- ST_ASSERT_EQ(e1, 0);
- ST_ASSERT_EQ(e2, 0);
- ST_ASSERT(p1.de == p2.de);
- }
- st_pass();
-
- /* ---- dotdot path: /dev/.. resolves to root ---- */
- st_begin("namei: /dev/.. resolves to root");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/dev/..", 0, &p);
- ST_ASSERT_EQ(err, 0);
- ST_ASSERT(p.de == g_root_dentry);
- }
- st_pass();
-
- /* ---- lookup into non-dir returns -ENOTDIR ---- */
- st_begin("namei: path through non-dir returns -ENOTDIR");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/dev/console/child", 0, &p);
- ST_ASSERT_EQ(err, -ENOTDIR);
- }
- st_pass();
-
- /* ---- null/empty paths ---- */
- st_begin("namei: null name returns -EINVAL");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, nullptr, 0, &p);
- ST_ASSERT_EQ(err, -EINVAL);
- }
- st_pass();
-
- /* ---- LOOKUP_PARENT: returns parent dentry ---- */
- st_begin("namei: LOOKUP_PARENT returns parent");
- {
- path p{};
- int err = filename_lookup(AT_FDCWD, "/dev/console", LOOKUP_PARENT, &p);
- ST_ASSERT_EQ(err, 0);
- /* Should return /dev, not /dev/console */
- path dev_path{};
- int e2 = filename_lookup(AT_FDCWD, "/dev", 0, &dev_path);
- ST_ASSERT_EQ(e2, 0);
- ST_ASSERT(p.de == dev_path.de);
- }
- st_pass();
-
- /* ---- mkdir + lookup round-trip ---- */
- st_begin("namei: mkdir then lookup succeeds");
- {
- /* Create /test_namei_dir via sys_mkdirat */
- int64 ret = sys_mkdirat(
- static_cast<uint64>(-100),
- reinterpret_cast<uint64>("/test_namei_dir"),
- 0755, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)0);
-
- /* Now lookup should find it */
- path p{};
- int err = filename_lookup(AT_FDCWD, "/test_namei_dir", 0, &p);
- ST_ASSERT_EQ(err, 0);
- ST_ASSERT(p.de->d_inode != nullptr);
- ST_ASSERT(S_ISDIR(p.de->d_inode->i_mode));
- }
- st_pass();
-
- /* NOTE: trailing slash "/dev/" not tested — Phase 1 namei
- * doesn't handle empty trailing components. */
-}
diff --git a/na/test/test_page_cache.cc b/na/test/test_page_cache.cc
deleted file mode 100644
index ee0e0e2..0000000
--- a/na/test/test_page_cache.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_page_cache.cc — Self-tests for the page cache hash table
- *
- * Tests: insert, get, remove, duplicate insert, invalidate,
- * get_or_alloc, null args.
- */
-
-#include <lume/selftest.h>
-#include <lume/page_cache.h>
-#include <lume/page.h>
-#include <lume/pmm.h>
-#include <lume/errno.h>
-
-void selftest_page_cache()
-{
- /* Use sentinel pointers as fake "mapping" identifiers.
- * These are never dereferenced — only used as hash keys. */
- int mapping_a_tag = 0;
- int mapping_b_tag = 0;
- void* mapping_a = &mapping_a_tag;
- void* mapping_b = &mapping_b_tag;
-
- /* ---- insert and get ---- */
- st_begin("page_cache: insert then get returns same page");
- {
- Page* page = pmm_alloc_page();
- ST_ASSERT(page != nullptr);
-
- int ret = page_cache_insert(mapping_a, 0, page);
- ST_ASSERT_EQ(ret, 0);
-
- Page* got = page_cache_get(mapping_a, 0);
- ST_ASSERT_EQ(got, page);
-
- /* put the reference from get */
- page_cache_put(got);
-
- /* Clean up */
- page_cache_remove(mapping_a, 0);
- pmm_free_page(page);
- }
- st_pass();
-
- /* ---- get nonexistent returns nullptr ---- */
- st_begin("page_cache: get nonexistent returns nullptr");
- {
- Page* got = page_cache_get(mapping_a, 99999);
- ST_ASSERT(got == nullptr);
- }
- st_pass();
-
- /* ---- duplicate insert returns -EEXIST ---- */
- st_begin("page_cache: duplicate insert returns -EEXIST");
- {
- Page* page = pmm_alloc_page();
- ST_ASSERT(page != nullptr);
-
- int ret1 = page_cache_insert(mapping_a, 1, page);
- ST_ASSERT_EQ(ret1, 0);
-
- Page* page2 = pmm_alloc_page();
- ST_ASSERT(page2 != nullptr);
-
- int ret2 = page_cache_insert(mapping_a, 1, page2);
- ST_ASSERT_EQ(ret2, -EEXIST);
-
- /* Clean up */
- page_cache_remove(mapping_a, 1);
- pmm_free_page(page);
- pmm_free_page(page2);
- }
- st_pass();
-
- /* ---- remove returns page ---- */
- st_begin("page_cache: remove returns page and deletes entry");
- {
- Page* page = pmm_alloc_page();
- ST_ASSERT(page != nullptr);
-
- page_cache_insert(mapping_a, 2, page);
-
- Page* removed = page_cache_remove(mapping_a, 2);
- ST_ASSERT_EQ(removed, page);
-
- /* Should be gone now */
- Page* got = page_cache_get(mapping_a, 2);
- ST_ASSERT(got == nullptr);
-
- pmm_free_page(page);
- }
- st_pass();
-
- /* ---- remove nonexistent returns nullptr ---- */
- st_begin("page_cache: remove nonexistent returns nullptr");
- {
- Page* got = page_cache_remove(mapping_a, 77777);
- ST_ASSERT(got == nullptr);
- }
- st_pass();
-
- /* ---- different mappings are independent ---- */
- st_begin("page_cache: different mappings are independent");
- {
- Page* pa = pmm_alloc_page();
- Page* pb = pmm_alloc_page();
- ST_ASSERT(pa != nullptr);
- ST_ASSERT(pb != nullptr);
-
- page_cache_insert(mapping_a, 5, pa);
- page_cache_insert(mapping_b, 5, pb);
-
- Page* got_a = page_cache_get(mapping_a, 5);
- Page* got_b = page_cache_get(mapping_b, 5);
- ST_ASSERT_EQ(got_a, pa);
- ST_ASSERT_EQ(got_b, pb);
- ST_ASSERT_NE(got_a, got_b);
-
- page_cache_put(got_a);
- page_cache_put(got_b);
- page_cache_remove(mapping_a, 5);
- page_cache_remove(mapping_b, 5);
- pmm_free_page(pa);
- pmm_free_page(pb);
- }
- st_pass();
-
- /* ---- invalidate removes all entries for a mapping ---- */
- st_begin("page_cache: invalidate removes all entries for mapping");
- {
- Page* pages[4];
- for (int i = 0; i < 4; i++) {
- pages[i] = pmm_alloc_page();
- ST_ASSERT(pages[i] != nullptr);
- page_cache_insert(mapping_a, 10 + i, pages[i]);
- }
-
- /* Also insert one for mapping_b */
- Page* pb = pmm_alloc_page();
- page_cache_insert(mapping_b, 10, pb);
-
- /* Invalidate mapping_a */
- page_cache_invalidate(mapping_a);
-
- /* All mapping_a entries should be gone */
- for (int i = 0; i < 4; i++) {
- Page* got = page_cache_get(mapping_a, 10 + i);
- ST_ASSERT(got == nullptr);
- pmm_free_page(pages[i]);
- }
-
- /* mapping_b should still be there */
- Page* got_b = page_cache_get(mapping_b, 10);
- ST_ASSERT_EQ(got_b, pb);
- page_cache_put(got_b);
- page_cache_remove(mapping_b, 10);
- pmm_free_page(pb);
- }
- st_pass();
-
- /* ---- insert null mapping returns -EINVAL ---- */
- st_begin("page_cache: insert null mapping returns -EINVAL");
- {
- Page* page = pmm_alloc_page();
- int ret = page_cache_insert(nullptr, 0, page);
- ST_ASSERT_EQ(ret, -EINVAL);
- pmm_free_page(page);
- }
- st_pass();
-
- /* ---- insert null page returns -EINVAL ---- */
- st_begin("page_cache: insert null page returns -EINVAL");
- {
- int ret = page_cache_insert(mapping_a, 0, nullptr);
- ST_ASSERT_EQ(ret, -EINVAL);
- }
- st_pass();
-}
diff --git a/na/test/test_pid.cc b/na/test/test_pid.cc
deleted file mode 100644
index e79eb1e..0000000
--- a/na/test/test_pid.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_pid.cc — Self-tests for PID allocator
- *
- * Tests: alloc, free, re-use, exhaustion guard, reserved PIDs.
- */
-
-#include <lume/selftest.h>
-#include <lume/pid.h>
-
-void selftest_pid()
-{
- /* Create a local PidAllocator for isolated testing */
- PidAllocator pa;
- pa.init();
-
- /* ---- PID 0 is reserved (idle/swapper) ---- */
- st_begin("pid: PID 0 is reserved");
- {
- /* First alloc should return 1 (PID 0 reserved for idle) */
- int32 first = pa.alloc();
- ST_ASSERT(first >= 1);
- }
- st_pass();
-
- /* ---- Sequential allocation returns unique PIDs ---- */
- st_begin("pid: sequential allocation returns unique PIDs");
- {
- PidAllocator pa2;
- pa2.init();
-
- int32 p1 = pa2.alloc();
- int32 p2 = pa2.alloc();
- int32 p3 = pa2.alloc();
- ST_ASSERT(p1 >= 1);
- ST_ASSERT(p2 >= 1);
- ST_ASSERT(p3 >= 1);
- ST_ASSERT_NE(p1, p2);
- ST_ASSERT_NE(p2, p3);
- ST_ASSERT_NE(p1, p3);
- }
- st_pass();
-
- /* ---- Freed PID can be reused ---- */
- st_begin("pid: freed PID can be reused");
- {
- PidAllocator pa3;
- pa3.init();
-
- /* Allocate all PIDs from 2..N */
- constexpr int kAllocCount = 60;
- int32 pids[kAllocCount];
- for (int i = 0; i < kAllocCount; i++) {
- pids[i] = pa3.alloc();
- ST_ASSERT(pids[i] >= 1);
- }
-
- /* Free one in the middle */
- int32 freed_pid = pids[kAllocCount / 2];
- pa3.free(freed_pid);
-
- /* Allocate again — should eventually get the freed PID */
- bool found = false;
- for (int i = 0; i < kAllocCount; i++) {
- int32 p = pa3.alloc();
- if (p == freed_pid) { found = true; break; }
- pa3.free(p);
- }
- ST_ASSERT(found);
- }
- st_pass();
-
- /* ---- free invalid PID is safe ---- */
- st_begin("pid: free invalid PID is no-op");
- {
- PidAllocator pa4;
- pa4.init();
-
- /* These should not crash */
- pa4.free(-1);
- pa4.free(kMaxPid);
- pa4.free(kMaxPid + 100);
-
- /* Allocator should still work — PID 0 still reserved */
- int32 p = pa4.alloc();
- ST_ASSERT(p >= 1);
- }
- st_pass();
-
- /* ---- Monotonic allocation before wrap ---- */
- st_begin("pid: monotonic allocation");
- {
- PidAllocator pa5;
- pa5.init();
-
- int32 prev = pa5.alloc();
- for (int i = 0; i < 10; i++) {
- int32 next = pa5.alloc();
- ST_ASSERT(next > prev);
- prev = next;
- }
- }
- st_pass();
-}
diff --git a/na/test/test_pipe_devfs.cc b/na/test/test_pipe_devfs.cc
deleted file mode 100644
index ed375ed..0000000
--- a/na/test/test_pipe_devfs.cc
+++ /dev/null
@@ -1,420 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_pipe_devfs.cc — Self-tests for pipe, /dev/null, /dev/zero, fcntl
- */
-
-#include <lume/selftest.h>
-#include <lume/errno.h>
-#include <lume/file.h>
-#include <lume/fs.h>
-#include <lume/fs_types.h>
-#include <lume/task.h>
-#include <lume/fdtable.h>
-#include <lume/pipe.h>
-
-/* Syscall entry points */
-int64 sys_openat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_close(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_read(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_write(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_pipe2(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_fcntl(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_fstat_wrapper(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* ================================================================== */
-/* Helpers */
-/* ================================================================== */
-
-static int64 open_dev(const char* path, int flags)
-{
- return sys_openat(
- static_cast<uint64>(-100), /* AT_FDCWD */
- reinterpret_cast<uint64>(path),
- static_cast<uint64>(flags),
- 0, 0, 0);
-}
-
-static int64 do_close(int64 fd)
-{
- return sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
-}
-
-static int64 do_read(int64 fd, void* buf, uint64 count)
-{
- return sys_read(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf), count, 0, 0, 0);
-}
-
-static int64 do_write(int64 fd, const void* buf, uint64 count)
-{
- return sys_write(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf), count, 0, 0, 0);
-}
-
-/* fcntl command constants — must match sys_fs.cc */
-inline constexpr int F_DUPFD = 0;
-inline constexpr int F_GETFD = 1;
-inline constexpr int F_SETFD = 2;
-inline constexpr int F_GETFL = 3;
-inline constexpr int F_SETFL = 4;
-inline constexpr int F_DUPFD_CLOEXEC = 1030;
-inline constexpr int FD_CLOEXEC = 1;
-
-/* ================================================================== */
-/* /dev/null tests */
-/* ================================================================== */
-
-void selftest_devfs()
-{
- /* ---- /dev/null: read returns EOF ---- */
- st_begin("devfs: /dev/null read returns 0 (EOF)");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- char buf[16];
- int64 n = do_read(fd, buf, sizeof(buf));
- ST_ASSERT_EQ(n, (int64)0);
- do_close(fd);
- }
- st_pass();
-
- /* ---- /dev/null: write discards data ---- */
- st_begin("devfs: /dev/null write discards data");
- {
- int64 fd = open_dev("/dev/null", O_WRONLY);
- ST_ASSERT(fd >= 0);
-
- const char* msg = "discarded";
- int64 n = do_write(fd, msg, 9);
- ST_ASSERT_EQ(n, (int64)9);
- do_close(fd);
- }
- st_pass();
-
- /* ---- /dev/null: fstat reports S_IFCHR ---- */
- st_begin("devfs: /dev/null fstat is S_IFCHR");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- kstat st{};
- int64 ret = sys_fstat_wrapper(
- static_cast<uint64>(fd),
- reinterpret_cast<uint64>(&st), 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)0);
- ST_ASSERT(S_ISCHR(st.st_mode));
- do_close(fd);
- }
- st_pass();
-
- /* ---- /dev/zero: read returns zeros ---- */
- st_begin("devfs: /dev/zero read returns zeros");
- {
- int64 fd = open_dev("/dev/zero", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- char buf[32];
- /* Pre-fill with non-zero */
- for (int i = 0; i < 32; i++) buf[i] = 0xFF;
-
- int64 n = do_read(fd, buf, 32);
- ST_ASSERT_EQ(n, (int64)32);
-
- bool all_zero = true;
- for (int i = 0; i < 32; i++) {
- if (buf[i] != 0) { all_zero = false; break; }
- }
- ST_ASSERT(all_zero);
- do_close(fd);
- }
- st_pass();
-
- /* ---- /dev/zero: write discards data ---- */
- st_begin("devfs: /dev/zero write discards data");
- {
- int64 fd = open_dev("/dev/zero", O_WRONLY);
- ST_ASSERT(fd >= 0);
-
- char buf[16] = {1, 2, 3};
- int64 n = do_write(fd, buf, 16);
- ST_ASSERT_EQ(n, (int64)16);
- do_close(fd);
- }
- st_pass();
-}
-
-/* ================================================================== */
-/* Pipe tests */
-/* ================================================================== */
-
-void selftest_pipe()
-{
- /* ---- pipe2: basic creation ---- */
- st_begin("pipe: pipe2 creates two valid fds");
- {
- int fds[2] = {-1, -1};
- int64 ret = sys_pipe2(
- reinterpret_cast<uint64>(fds), 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)0);
- ST_ASSERT(fds[0] >= 0);
- ST_ASSERT(fds[1] >= 0);
- ST_ASSERT_NE(fds[0], fds[1]);
-
- do_close(fds[0]);
- do_close(fds[1]);
- }
- st_pass();
-
- /* ---- pipe: write then read ---- */
- st_begin("pipe: write then read returns correct data");
- {
- int fds[2];
- sys_pipe2(reinterpret_cast<uint64>(fds), 0, 0, 0, 0, 0);
-
- const char* msg = "pipe_test";
- int64 w = do_write(fds[1], msg, 9);
- ST_ASSERT_EQ(w, (int64)9);
-
- char buf[16] = {};
- int64 r = do_read(fds[0], buf, 16);
- ST_ASSERT_EQ(r, (int64)9);
-
- bool match = true;
- for (int i = 0; i < 9; i++) {
- if (buf[i] != msg[i]) { match = false; break; }
- }
- ST_ASSERT(match);
-
- do_close(fds[0]);
- do_close(fds[1]);
- }
- st_pass();
-
- /* ---- pipe: read from closed write end returns EOF ---- */
- st_begin("pipe: read after writer close returns EOF");
- {
- int fds[2];
- sys_pipe2(reinterpret_cast<uint64>(fds), 0, 0, 0, 0, 0);
-
- /* Write some data, then close write end */
- const char* msg = "eof";
- do_write(fds[1], msg, 3);
- do_close(fds[1]);
-
- /* Read the data */
- char buf[16];
- int64 r1 = do_read(fds[0], buf, 16);
- ST_ASSERT_EQ(r1, (int64)3);
-
- /* Next read should return 0 (EOF) */
- int64 r2 = do_read(fds[0], buf, 16);
- ST_ASSERT_EQ(r2, (int64)0);
-
- do_close(fds[0]);
- }
- st_pass();
-
- /* ---- pipe: write to closed read end returns -EPIPE ---- */
- st_begin("pipe: write after reader close returns -EPIPE");
- {
- int fds[2];
- sys_pipe2(reinterpret_cast<uint64>(fds), 0, 0, 0, 0, 0);
-
- /* Close read end */
- do_close(fds[0]);
-
- /* Write should fail */
- const char* msg = "broken";
- int64 w = do_write(fds[1], msg, 6);
- ST_ASSERT_EQ(w, (int64)-EPIPE);
-
- do_close(fds[1]);
- }
- st_pass();
-
- /* ---- pipe: multiple writes accumulate ---- */
- st_begin("pipe: multiple writes accumulate in buffer");
- {
- int fds[2];
- sys_pipe2(reinterpret_cast<uint64>(fds), 0, 0, 0, 0, 0);
-
- do_write(fds[1], "AAA", 3);
- do_write(fds[1], "BBB", 3);
-
- char buf[16] = {};
- int64 r = do_read(fds[0], buf, 16);
- ST_ASSERT_EQ(r, (int64)6);
- ST_ASSERT_EQ(buf[0], 'A');
- ST_ASSERT_EQ(buf[3], 'B');
-
- do_close(fds[0]);
- do_close(fds[1]);
- }
- st_pass();
-
- /* ---- pipe: read partial ---- */
- st_begin("pipe: partial read leaves remainder");
- {
- int fds[2];
- sys_pipe2(reinterpret_cast<uint64>(fds), 0, 0, 0, 0, 0);
-
- do_write(fds[1], "ABCDEF", 6);
-
- char buf[3] = {};
- int64 r1 = do_read(fds[0], buf, 3);
- ST_ASSERT_EQ(r1, (int64)3);
- ST_ASSERT_EQ(buf[0], 'A');
- ST_ASSERT_EQ(buf[2], 'C');
-
- int64 r2 = do_read(fds[0], buf, 3);
- ST_ASSERT_EQ(r2, (int64)3);
- ST_ASSERT_EQ(buf[0], 'D');
- ST_ASSERT_EQ(buf[2], 'F');
-
- do_close(fds[0]);
- do_close(fds[1]);
- }
- st_pass();
-}
-
-/* ================================================================== */
-/* fcntl tests */
-/* ================================================================== */
-
-void selftest_fcntl()
-{
- /* ---- fcntl F_DUPFD ---- */
- st_begin("fcntl: F_DUPFD duplicates fd");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- int64 fd2 = sys_fcntl(static_cast<uint64>(fd), F_DUPFD, 0, 0, 0, 0);
- ST_ASSERT(fd2 >= 0);
- ST_ASSERT_NE(fd, fd2);
-
- /* Both should be valid reads */
- char buf[4];
- int64 r = do_read(fd2, buf, 4);
- ST_ASSERT_EQ(r, (int64)0); /* /dev/null → EOF */
-
- do_close(fd);
- do_close(fd2);
- }
- st_pass();
-
- /* ---- fcntl F_DUPFD with min_fd ---- */
- st_begin("fcntl: F_DUPFD respects min_fd");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- int64 fd2 = sys_fcntl(static_cast<uint64>(fd), F_DUPFD, 100, 0, 0, 0);
- ST_ASSERT(fd2 >= 100);
-
- do_close(fd);
- do_close(fd2);
- }
- st_pass();
-
- /* ---- fcntl F_GETFD / F_SETFD ---- */
- st_begin("fcntl: F_GETFD/F_SETFD cloexec");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- /* Initially no CLOEXEC */
- int64 flags = sys_fcntl(static_cast<uint64>(fd), F_GETFD, 0, 0, 0, 0);
- ST_ASSERT_EQ(flags, (int64)0);
-
- /* Set CLOEXEC */
- int64 ret = sys_fcntl(static_cast<uint64>(fd), F_SETFD, FD_CLOEXEC, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)0);
-
- /* Verify it was set */
- flags = sys_fcntl(static_cast<uint64>(fd), F_GETFD, 0, 0, 0, 0);
- ST_ASSERT_EQ(flags, (int64)FD_CLOEXEC);
-
- /* Clear it */
- sys_fcntl(static_cast<uint64>(fd), F_SETFD, 0, 0, 0, 0);
- flags = sys_fcntl(static_cast<uint64>(fd), F_GETFD, 0, 0, 0, 0);
- ST_ASSERT_EQ(flags, (int64)0);
-
- do_close(fd);
- }
- st_pass();
-
- /* ---- fcntl F_GETFL ---- */
- st_begin("fcntl: F_GETFL returns open flags");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- int64 fl = sys_fcntl(static_cast<uint64>(fd), F_GETFL, 0, 0, 0, 0);
- /* O_RDONLY == 0, so result should have no write bits */
- ST_ASSERT_EQ(fl & O_WRONLY, (int64)0);
-
- do_close(fd);
- }
- st_pass();
-
- /* ---- fcntl F_SETFL O_NONBLOCK ---- */
- st_begin("fcntl: F_SETFL toggles O_NONBLOCK");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- /* Set O_NONBLOCK */
- sys_fcntl(static_cast<uint64>(fd), F_SETFL, O_NONBLOCK, 0, 0, 0);
- int64 fl = sys_fcntl(static_cast<uint64>(fd), F_GETFL, 0, 0, 0, 0);
- ST_ASSERT(fl & O_NONBLOCK);
-
- /* Clear it */
- sys_fcntl(static_cast<uint64>(fd), F_SETFL, 0, 0, 0, 0);
- fl = sys_fcntl(static_cast<uint64>(fd), F_GETFL, 0, 0, 0, 0);
- ST_ASSERT_EQ(fl & O_NONBLOCK, (int64)0);
-
- do_close(fd);
- }
- st_pass();
-
- /* ---- fcntl F_DUPFD_CLOEXEC ---- */
- st_begin("fcntl: F_DUPFD_CLOEXEC sets cloexec");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- int64 fd2 = sys_fcntl(static_cast<uint64>(fd), F_DUPFD_CLOEXEC, 0, 0, 0, 0);
- ST_ASSERT(fd2 >= 0);
-
- int64 flags = sys_fcntl(static_cast<uint64>(fd2), F_GETFD, 0, 0, 0, 0);
- ST_ASSERT_EQ(flags, (int64)FD_CLOEXEC);
-
- do_close(fd);
- do_close(fd2);
- }
- st_pass();
-
- /* ---- fcntl bad fd ---- */
- st_begin("fcntl: bad fd returns -EBADF");
- {
- int64 ret = sys_fcntl(999, F_GETFD, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-EBADF);
- }
- st_pass();
-
- /* ---- fcntl unknown cmd ---- */
- st_begin("fcntl: unknown cmd returns -EINVAL");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- int64 ret = sys_fcntl(static_cast<uint64>(fd), 9999, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-EINVAL);
- do_close(fd);
- }
- st_pass();
-}
diff --git a/na/test/test_pmm.cc b/na/test/test_pmm.cc
deleted file mode 100644
index 3330866..0000000
--- a/na/test/test_pmm.cc
+++ /dev/null
@@ -1,283 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot Self-Test: Physical Memory Manager (PMM)
- *
- * Tests:
- * 1. Single page alloc/free round-trip
- * 2. Page address alignment (4KB)
- * 3. No duplicate pages from consecutive allocs
- * 4. Free then re-alloc returns the same page (PCP cache)
- * 5. Multi-page (order > 0) alloc/free
- * 6. Bulk alloc stress test (alloc many, free all)
- * 7. Reference counting (incref/decref)
- * 8. page_to_pa / pa_to_page round-trip
- */
-
-#include <lume/selftest.h>
-#include <lume/pmm.h>
-#include <lume/addr.h>
-
-void selftest_pmm()
-{
- st_begin("pmm: single page alloc/free");
- {
- Page* page = pmm_alloc_page();
- ST_ASSERT(page != nullptr);
- ST_ASSERT(page->type == PageType::Allocated);
- ST_ASSERT(page->order == 0);
-
- uint64 pa = page_to_pa(page).raw;
- ST_ASSERT_EQ(pa % kPageSize, 0ULL); // 4KB aligned
-
- uint64 va = page_to_va(page).raw;
- ST_ASSERT_EQ(va, pa_to_va(phys_addr(pa)).raw);
-
- pmm_free_page(page);
- }
- st_pass();
-
- st_begin("pmm: no duplicate pages");
- {
- Page* p1 = pmm_alloc_page();
- Page* p2 = pmm_alloc_page();
- ST_ASSERT(p1 != nullptr);
- ST_ASSERT(p2 != nullptr);
- ST_ASSERT_NE(p1, p2);
- ST_ASSERT_NE(page_to_pa(p1).raw, page_to_pa(p2).raw);
- pmm_free_page(p2);
- pmm_free_page(p1);
- }
- st_pass();
-
- st_begin("pmm: free then re-alloc (PCP)");
- {
- Page* p1 = pmm_alloc_page();
- uint64 pa1 = page_to_pa(p1).raw;
- pmm_free_page(p1);
-
- // PCP should return the most recently freed page
- Page* p2 = pmm_alloc_page();
- uint64 pa2 = page_to_pa(p2).raw;
- ST_ASSERT_EQ(pa1, pa2);
- pmm_free_page(p2);
- }
- st_pass();
-
- st_begin("pmm: multi-page alloc (order 2 = 4 pages)");
- {
- Page* page = pmm_alloc_pages(2);
- ST_ASSERT(page != nullptr);
- ST_ASSERT(page->order == 2);
-
- uint64 pa = page_to_pa(page).raw;
- ST_ASSERT_EQ(pa % (kPageSize * 4), 0ULL); // Naturally aligned
-
- pmm_free_pages(page, 2);
- }
- st_pass();
-
- st_begin("pmm: bulk alloc/free stress (256 pages)");
- {
- constexpr int N = 256;
- Page* pages[N];
-
- // Allocate all
- for (int i = 0; i < N; i++) {
- pages[i] = pmm_alloc_page();
- ST_ASSERT(pages[i] != nullptr);
- }
-
- // Verify no duplicates (spot check: consecutive pairs)
- for (int i = 1; i < N; i++) {
- ST_ASSERT_NE(pages[i], pages[i - 1]);
- }
-
- // Free all
- for (int i = 0; i < N; i++) {
- pmm_free_page(pages[i]);
- }
- }
- st_pass();
-
- st_begin("pmm: page_to_pa / pa_to_page round-trip");
- {
- Page* page = pmm_alloc_page();
- ST_ASSERT(page != nullptr);
- uint64 pa = page_to_pa(page).raw;
- Page* page2 = pa_to_page(phys_addr(pa));
- ST_ASSERT_EQ(page, page2);
- pmm_free_page(page);
- }
- st_pass();
-
- st_begin("pmm: refcount incref/decref");
- {
- Page* page = pmm_alloc_page();
- ST_ASSERT(page != nullptr);
- ST_ASSERT(page->refcount.load() == 1);
-
- page_incref(page);
- ST_ASSERT(page->refcount.load() == 2);
-
- page_decref(page); // 2 -> 1, should NOT free
- ST_ASSERT(page->refcount.load() == 1);
- ST_ASSERT(page->type == PageType::Allocated);
-
- page_decref(page); // 1 -> 0, should free to pool
- // After decref to 0, page is returned to pool.
- // We can't safely check page->type because it might be reused.
- }
- st_pass();
- st_begin("pmm: pmm_alloc_pages natural alignment (order-3)");
- {
- Page* page = pmm_alloc_pages(3);
- ST_ASSERT(page != nullptr);
- uint64 pa = page_to_pa(page).raw;
- ST_ASSERT_EQ(pa % (kPageSize * 8), 0ULL);
- pmm_free_pages(page, 3);
- }
- st_pass();
-
- st_begin("pmm: page_decref to 0 and reuse (order-0)");
- {
- Page* page = pmm_alloc_page();
- uint64 pa = page_to_pa(page).raw;
- page_decref(page); // drops 1 -> 0, freeing it
-
- // Next alloc from PCP should give back the same page
- Page* page2 = pmm_alloc_page();
- ST_ASSERT_EQ(page_to_pa(page2).raw, pa);
- pmm_free_page(page2);
- }
- st_pass();
-
- st_begin("pmm: page_decref to 0 on multi-page blocks (order-3)");
- {
- Page* page = pmm_alloc_pages(3);
- uint64 pa = page_to_pa(page).raw;
- page_decref(page); // Should internally route to pmm_free_pages(page, 3)
-
- // Re-allocate order-3 and see if we get it back (buddy coalescing was respected)
- Page* page2 = pmm_alloc_pages(3);
- ST_ASSERT_EQ(page_to_pa(page2).raw, pa);
- pmm_free_pages(page2, 3);
- }
- st_pass();
-
- st_begin("pmm: buddy split and block coalescing correctness");
- {
- // Force split: allocate order-1, free it, allocate two order-0
- Page* page_ord1 = pmm_alloc_pages(1);
- uint64 pa1 = page_to_pa(page_ord1).raw;
- pmm_free_pages(page_ord1, 1);
-
- Page* p0_a = pmm_alloc_page();
- Page* p0_b = pmm_alloc_page();
-
- // Free them to trigger coalesce
- pmm_free_page(p0_a);
- pmm_free_page(p0_b);
-
- // Allocate order-1 again to verify successful coalescing
- Page* page_ord1_again = pmm_alloc_pages(1);
- ST_ASSERT_EQ(page_to_pa(page_ord1_again).raw, pa1);
- pmm_free_pages(page_ord1_again, 1);
- }
- st_pass();
-
- st_begin("pmm: mixed order cross allocation");
- {
- Page* p0 = pmm_alloc_page();
- Page* p2 = pmm_alloc_pages(2);
- Page* p0b = pmm_alloc_page();
-
- ST_ASSERT(p0 != nullptr);
- ST_ASSERT(p2 != nullptr);
- ST_ASSERT(p0b != nullptr);
- ST_ASSERT_NE(page_to_pa(p0).raw, page_to_pa(p2).raw);
-
- pmm_free_page(p0);
- pmm_free_pages(p2, 2);
- pmm_free_page(p0b);
- }
- st_pass();
-
- st_begin("pmm: PCP drain/refill boundaries");
- {
- // kPcpHighWatermark = 64. Allocating and freeing 65 blocks triggers drain.
- Page* pages[65];
- for (int i=0; i<65; i++) pages[i] = pmm_alloc_page();
- for (int i=0; i<65; i++) pmm_free_page(pages[i]);
-
- // The system shouldn't crash and PCP logic should handle this.
- Page* verify_page = pmm_alloc_page();
- ST_ASSERT(verify_page != nullptr);
- pmm_free_page(verify_page);
- }
- st_pass();
-
- st_begin("pmm: OOM exhaustion stability");
- {
- Page* head = nullptr;
- // First allocate large chunks to be fast
- while (true) {
- Page* page = pmm_alloc_pages(5);
- if (!page) break;
- uint64* ptr = reinterpret_cast<uint64*>(page_to_va(page).raw);
- *ptr = reinterpret_cast<uint64>(head);
- // encode order in the second word
- *(ptr + 1) = 5;
- head = page;
- }
- // Then allocate single frames until absolute zero
- while (true) {
- Page* page = pmm_alloc_page();
- if (!page) break;
- uint64* ptr = reinterpret_cast<uint64*>(page_to_va(page).raw);
- *ptr = reinterpret_cast<uint64>(head);
- *(ptr + 1) = 0;
- head = page;
- }
-
- // We hit nullptr. System is completely exhausted.
- ST_ASSERT(pmm_alloc_page() == nullptr);
-
- // Free everything
- while (head) {
- uint64* ptr = reinterpret_cast<uint64*>(page_to_va(head).raw);
- Page* nxt = reinterpret_cast<Page*>(*ptr);
- uint64 order = *(ptr + 1);
- if (order == 5) pmm_free_pages(head, 5);
- else pmm_free_page(head);
- head = nxt;
- }
- }
- st_pass();
-
- st_begin("pmm: out of bounds order allocation gracefully fails");
- {
- // kMaxOrder is 11, so passing 11 or higher should safely return nullptr.
- Page* page = pmm_alloc_pages(11);
- ST_ASSERT(page == nullptr);
- page = pmm_alloc_pages(255);
- ST_ASSERT(page == nullptr);
- }
- st_pass();
-
- st_begin("pmm: pmm_get_mem_base / pmm_get_mem_size sanity");
- {
- uint64 base = pmm_get_mem_base().raw;
- uint64 size = pmm_get_mem_size();
-
- // We no longer assert on QEMU specific memory address
- // ST_ASSERT(base >= 0x80000000ULL);
- ST_ASSERT(size > 0);
- // Size must be page-aligned
- ST_ASSERT_EQ(size % kPageSize, 0ULL);
- // Base must be page-aligned
- ST_ASSERT_EQ(base % kPageSize, 0ULL);
- }
- st_pass();
-}
diff --git a/na/test/test_random.cc b/na/test/test_random.cc
deleted file mode 100644
index d546410..0000000
--- a/na/test/test_random.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <lume/selftest.h>
-#include <lume/random.h>
-#include <lume/kprintf.h>
-
-void selftest_random()
-{
- /* Test 1: Basic 64-bit generation */
- kprintf("[test] random: basic 64-bit generation ... ");
- uint64 v1 = get_random_u64();
- uint64 v2 = get_random_u64();
- if (v1 == v2) {
- kernel_panic("random: two consecutive generations were identical!");
- }
- kprintf("PASS\n");
-
- /* Test 2: Basic 32-bit generation */
- kprintf("[test] random: basic 32-bit generation ... ");
- uint32 v3 = get_random_u32();
- uint32 v4 = get_random_u32();
- if (v3 == v4) {
- kernel_panic("random: two consecutive 32-bit generations were identical!");
- }
- kprintf("PASS\n");
-
- /* Test 3: Byte generation */
- kprintf("[test] random: byte generation ... ");
- uint8 buf1[16];
- uint8 buf2[16];
- get_random_bytes(buf1, sizeof(buf1));
- get_random_bytes(buf2, sizeof(buf2));
-
- bool all_zero1 = true;
- bool all_zero2 = true;
- bool same = true;
-
- for (int i = 0; i < 16; i++) {
- if (buf1[i] != 0) all_zero1 = false;
- if (buf2[i] != 0) all_zero2 = false;
- if (buf1[i] != buf2[i]) same = false;
- }
-
- if (all_zero1 || all_zero2 || same) {
- kernel_panic("random: byte generation statistical failure!");
- }
- kprintf("PASS\n");
-}
diff --git a/na/test/test_rbtree.cc b/na/test/test_rbtree.cc
deleted file mode 100644
index 27f049a..0000000
--- a/na/test/test_rbtree.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <lume/selftest.h>
-#include <lume/rbtree.h>
-
-struct TestNode {
- int key;
- struct rb_node rb;
-};
-
-static bool node_less(struct rb_node *node, const struct rb_node *parent) {
- TestNode *n = rb_entry(node, TestNode, rb);
- const TestNode *p = reinterpret_cast<const TestNode*>(
- reinterpret_cast<const char*>(parent) - __builtin_offsetof(TestNode, rb));
- return n->key < p->key;
-}
-
-static int node_cmp(const void *key, const struct rb_node *node) {
- int k = *static_cast<const int*>(key);
- const TestNode *n = reinterpret_cast<const TestNode*>(
- reinterpret_cast<const char*>(node) - __builtin_offsetof(TestNode, rb));
- if (k < n->key) return -1;
- if (k > n->key) return 1;
- return 0;
-}
-
-void selftest_rbtree() {
- st_begin("rbtree: basic insert, find, erase");
- {
- struct rb_root tree = RB_ROOT;
- TestNode n1{10, {}}, n2{20, {}}, n3{5, {}};
-
- rb_add(&n1.rb, &tree, node_less);
- rb_add(&n2.rb, &tree, node_less);
- rb_add(&n3.rb, &tree, node_less);
-
- int search_key = 20;
- struct rb_node *found = rb_find(&search_key, &tree, node_cmp);
- ST_ASSERT(found == &n2.rb);
-
- search_key = 99;
- found = rb_find(&search_key, &tree, node_cmp);
- ST_ASSERT(found == nullptr);
-
- rb_erase(&n2.rb, &tree);
- // Verify erased
- search_key = 20;
- found = rb_find(&search_key, &tree, node_cmp);
- ST_ASSERT(found == nullptr);
- }
- st_pass();
-
- st_begin("rbtree: ordered iteration");
- {
- struct rb_root tree = RB_ROOT;
- TestNode nodes[5] = { {50, {}}, {10, {}}, {40, {}}, {20, {}}, {30, {}} };
- for (int i=0; i<5; i++) {
- rb_add(&nodes[i].rb, &tree, node_less);
- }
-
- int expected[] = {10, 20, 30, 40, 50};
- int idx = 0;
- struct rb_node *node = rb_first(&tree);
- while (node) {
- TestNode *n = rb_entry(node, TestNode, rb);
- ST_ASSERT_EQ(n->key, expected[idx]);
- idx++;
- node = rb_next(node);
- }
- ST_ASSERT_EQ(idx, 5);
- }
- st_pass();
-
- st_begin("rbtree: large scale insert/erase balance");
- {
- struct rb_root tree = RB_ROOT;
- constexpr int N = 200;
- static TestNode nodes[N]; // Static to avoid blowing up the BSP stack
- for (int i=0; i<N; i++) {
- nodes[i].key = (i * 17) % N; // Pseudo-random insertions
- rb_add(&nodes[i].rb, &tree, node_less);
- }
-
- int c = 0;
- for (struct rb_node *n = rb_first(&tree); n; n = rb_next(n)) c++;
- ST_ASSERT_EQ(c, N);
-
- for (int i=0; i<N; i+=2) {
- rb_erase(&nodes[i].rb, &tree);
- }
- c = 0;
- for (struct rb_node *n = rb_first(&tree); n; n = rb_next(n)) c++;
- ST_ASSERT_EQ(c, N / 2);
- }
- st_pass();
-
- st_begin("rbtree: empty tree operations");
- {
- struct rb_root tree = RB_ROOT;
- ST_ASSERT(RB_EMPTY_ROOT(&tree));
- ST_ASSERT(rb_first(&tree) == nullptr);
- ST_ASSERT(rb_last(&tree) == nullptr);
- }
- st_pass();
-
- st_begin("rbtree: rb_erase_init after effects");
- {
- struct rb_root tree = RB_ROOT;
- TestNode n1{100, {}};
- rb_add(&n1.rb, &tree, node_less);
- ST_ASSERT(!RB_EMPTY_NODE(&n1.rb));
-
- rb_erase_init(&n1.rb, &tree);
- ST_ASSERT(RB_EMPTY_NODE(&n1.rb));
- ST_ASSERT(RB_EMPTY_ROOT(&tree));
- }
- st_pass();
-}
diff --git a/na/test/test_sched.cc b/na/test/test_sched.cc
deleted file mode 100644
index d90c30a..0000000
--- a/na/test/test_sched.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot self-tests for the Scheduler subsystem.
- *
- * Tests RunQueue bitmap operations, priority selection,
- * round-robin ordering, current_task(), and time slice policy.
- */
-
-#include <lume/types.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-#include <lume/sched_class.h>
-#include <lume/config.h>
-#include <lume/selftest.h>
-#include <lume/pmm.h>
-#include <lume/addr.h>
-#include <lume/new.h>
-#include <kernel/sched_internal.h>
-
-/* ========================================================================
- * Helper: create a minimal test TCB (no real kernel stack, just metadata)
- * ======================================================================== */
-
-static TaskControlBlock g_test_tcbs[8];
-
-static TaskControlBlock* make_test_tcb(int index, const char* name,
- uint8 priority, uint8 cpu)
-{
- TaskControlBlock* t = &g_test_tcbs[index];
- t->context = {};
- t->wait_chld.init("test_wq");
- t->name = name;
- t->priority = priority;
- t->cpu_id = cpu;
- t->pid = 100 + index;
- t->tgid = 100 + index;
- t->state = TaskState::Runnable;
- t->time_slice = priority_to_timeslice(priority);
- t->run_link.init();
- t->all_link.init();
- t->children.init();
- t->sibling.init();
- t->mm = nullptr;
- t->trapframe = nullptr;
- t->kstack_pa = 0;
- t->kstack_va = 0;
- t->flags = TF_KTHREAD;
- t->sched_class = &g_sched_class_o1;
- t->sched_entity = &t->run_link;
- return t;
-}
-
-/* ========================================================================
- * Test cases
- * ======================================================================== */
-
-static bool test_current_task()
-{
- /* After sched_init, current_task() should be the BSP boot TCB */
- TaskControlBlock* curr = current_task();
- return curr != nullptr && curr->state == TaskState::Running;
-}
-
-static bool test_priority_to_timeslice()
-{
- /* Higher priority (lower number) should get longer time slice */
- int32 ts_high = priority_to_timeslice(0);
- int32 ts_mid = priority_to_timeslice(15);
- int32 ts_low = priority_to_timeslice(31);
-
- return ts_high > ts_mid && ts_mid > ts_low && ts_low >= 1;
-}
-
-static bool test_o1_pick_next_priority()
-{
- RunQueue rq;
- rq.init(0);
- g_sched_class_o1.init_rq(&rq);
-
- TaskControlBlock* t_lo = make_test_tcb(0, "test_p5", 5, 0);
- TaskControlBlock* t_hi = make_test_tcb(1, "test_p2", 2, 0);
-
- g_sched_class_o1.enqueue_task(&rq, t_lo);
- g_sched_class_o1.enqueue_task(&rq, t_hi);
-
- TaskControlBlock* next = g_sched_class_o1.pick_next_task(&rq);
- return next == t_hi;
-}
-
-static bool test_o1_fifo_order()
-{
- RunQueue rq;
- rq.init(0);
- g_sched_class_o1.init_rq(&rq);
-
- TaskControlBlock* t0 = make_test_tcb(0, "test_q0", 10, 0);
- TaskControlBlock* t1 = make_test_tcb(1, "test_q1", 10, 0);
- TaskControlBlock* t2 = make_test_tcb(2, "test_q2", 10, 0);
-
- g_sched_class_o1.enqueue_task(&rq, t0);
- g_sched_class_o1.enqueue_task(&rq, t1);
- g_sched_class_o1.enqueue_task(&rq, t2);
-
- TaskControlBlock* next = g_sched_class_o1.pick_next_task(&rq);
- if (next != t0) return false;
- g_sched_class_o1.dequeue_task(&rq, t0);
-
- next = g_sched_class_o1.pick_next_task(&rq);
- if (next != t1) return false;
- g_sched_class_o1.dequeue_task(&rq, t1);
-
- next = g_sched_class_o1.pick_next_task(&rq);
- if (next != t2) return false;
-
- return true;
-}
-
-static bool test_o1_nr_running()
-{
- RunQueue rq;
- rq.init(0);
- g_sched_class_o1.init_rq(&rq);
-
- TaskControlBlock* t0 = make_test_tcb(0, "test_n0", 8, 0);
- TaskControlBlock* t1 = make_test_tcb(1, "test_n1", 9, 0);
-
- if (rq.nr_running() != 0 || rq.o1().nr_running() != 0) return false;
-
- g_sched_class_o1.enqueue_task(&rq, t0);
- if (rq.nr_running() != 1 || rq.o1().nr_running() != 1) return false;
-
- g_sched_class_o1.enqueue_task(&rq, t1);
- if (rq.nr_running() != 2 || rq.o1().nr_running() != 2) return false;
-
- g_sched_class_o1.dequeue_task(&rq, t0);
- if (rq.nr_running() != 1 || rq.o1().nr_running() != 1) return false;
-
- g_sched_class_o1.dequeue_task(&rq, t1);
- return rq.nr_running() == 0 && rq.o1().nr_running() == 0;
-}
-
-static bool test_o1_pick_next_empty()
-{
- RunQueue rq;
- rq.init(0);
- g_sched_class_o1.init_rq(&rq);
-
- return g_sched_class_o1.pick_next_task(&rq) == nullptr;
-}
-
-static bool test_priority_to_timeslice_bounds()
-{
- /* Boundary values */
- int32 ts0 = priority_to_timeslice(0);
- int32 ts31 = priority_to_timeslice(31);
-
- return ts0 == 20 && ts31 >= 1;
-}
-
-static bool test_enqueue_dequeue()
-{
- /* Create a test task and enqueue/dequeue it */
- TaskControlBlock* t = make_test_tcb(0, "test_ed", 10, 0);
-
- sched_enqueue(t);
- if (t->state != TaskState::Runnable) return false;
-
- sched_dequeue(t);
- /* After dequeue, run_link should be self-pointing */
- return t->run_link.next == &t->run_link;
-}
-
-static bool test_multiple_enqueue_dequeue()
-{
- /* Enqueue 3 tasks at different priorities, dequeue all */
- TaskControlBlock* t0 = make_test_tcb(0, "test_m0", 5, 0);
- TaskControlBlock* t1 = make_test_tcb(1, "test_m1", 10, 0);
- TaskControlBlock* t2 = make_test_tcb(2, "test_m2", 20, 0);
-
- sched_enqueue(t0);
- sched_enqueue(t1);
- sched_enqueue(t2);
-
- sched_dequeue(t0);
- sched_dequeue(t1);
- sched_dequeue(t2);
-
- return t0->run_link.is_empty() &&
- t1->run_link.is_empty() &&
- t2->run_link.is_empty();
-}
-
-static bool test_current_task_name()
-{
- TaskControlBlock* curr = current_task();
- if (!curr || !curr->name) return false;
-
- /* BSP boot TCB should be named "kernel_main" */
- const char* expected = "kernel_main";
- const char* actual = curr->name;
- while (*expected && *actual) {
- if (*expected != *actual) return false;
- expected++;
- actual++;
- }
- return *expected == *actual;
-}
-
-/* ========================================================================
- * Test runner
- * ======================================================================== */
-
-void selftest_sched()
-{
- st_begin("sched: current_task is valid after init");
- ST_ASSERT(test_current_task());
- st_pass();
-
- st_begin("sched: current_task name is kernel_main");
- ST_ASSERT(test_current_task_name());
- st_pass();
-
- st_begin("sched: priority_to_timeslice ordering");
- ST_ASSERT(test_priority_to_timeslice());
- st_pass();
-
- st_begin("sched: priority_to_timeslice bounds");
- ST_ASSERT(test_priority_to_timeslice_bounds());
- st_pass();
-
- st_begin("sched: enqueue/dequeue round-trip");
- ST_ASSERT(test_enqueue_dequeue());
- st_pass();
-
- st_begin("sched: multiple enqueue/dequeue");
- ST_ASSERT(test_multiple_enqueue_dequeue());
- st_pass();
-
- st_begin("sched: o1 pick_next priority");
- ST_ASSERT(test_o1_pick_next_priority());
- st_pass();
-
- st_begin("sched: o1 FIFO order");
- ST_ASSERT(test_o1_fifo_order());
- st_pass();
-
- st_begin("sched: o1 nr_running accounting");
- ST_ASSERT(test_o1_nr_running());
- st_pass();
-
- st_begin("sched: o1 pick_next empty");
- ST_ASSERT(test_o1_pick_next_empty());
- st_pass();
-}
-
diff --git a/na/test/test_signal.cc b/na/test/test_signal.cc
deleted file mode 100644
index 360b0e6..0000000
--- a/na/test/test_signal.cc
+++ /dev/null
@@ -1,193 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_signal.cc — Self-tests for signal infrastructure
- *
- * Tests the kernel-internal signal APIs directly (not via syscalls,
- * since syscalls use copy_to_user which rejects kernel-space buffers).
- */
-
-#include <lume/selftest.h>
-#include <lume/signal.h>
-#include <lume/signal_types.h>
-#include <lume/signal_nr.h>
-#include <lume/task.h>
-#include <lume/errno.h>
-#include <lume/new.h>
-
-/* From signal.cc */
-extern int send_signal(int sig, kernel_siginfo* info, TaskControlBlock* task);
-
-/* Drain all queued sigqueue entries from a task's pending list */
-static void flush_pending(TaskControlBlock* task)
-{
- task->pending.signal = sigset_t{};
- list_node* head = &task->pending.list;
- while (head->next != head) {
- list_node* node = head->next;
- head->next = node->next;
- node->next->prev = head;
- auto* q = list_entry<sigqueue, &sigqueue::list>(node);
- delete q;
- }
-}
-
-void selftest_signal()
-{
- TaskControlBlock* curr = current_task();
-
- /* Ensure sighand exists for this task */
- if (!curr->sighand) {
- curr->sighand = new sighand_struct;
- curr->sighand->init();
- }
-
- /* Ensure pending list is initialized */
- if (curr->pending.list.next == nullptr) {
- curr->pending.list.next = &curr->pending.list;
- curr->pending.list.prev = &curr->pending.list;
- curr->pending.signal = sigset_t{};
- }
-
- /* ---- send_signal queues a signal ---- */
- st_begin("signal: send_signal queues to pending");
- {
- curr->pending.signal = sigset_t{};
-
- kernel_siginfo info{};
- info.si_signo = SIGUSR1;
- info.si_code = 0;
-
- int ret = send_signal(SIGUSR1, &info, curr);
- ST_ASSERT_EQ(ret, 0);
- ST_ASSERT(curr->pending.signal.has(SIGUSR1));
-
- flush_pending(curr);
- }
- st_pass();
-
- /* ---- send_signal: invalid signal returns -EINVAL ---- */
- st_begin("signal: send_signal invalid sig returns -EINVAL");
- {
- int ret = send_signal(0, nullptr, curr);
- ST_ASSERT_EQ(ret, -EINVAL);
-
- ret = send_signal(_NSIG + 1, nullptr, curr);
- ST_ASSERT_EQ(ret, -EINVAL);
- }
- st_pass();
-
- /* ---- send_signal: null task returns -ESRCH ---- */
- st_begin("signal: send_signal null task returns -ESRCH");
- {
- int ret = send_signal(SIGUSR1, nullptr, nullptr);
- ST_ASSERT_EQ(ret, -ESRCH);
- }
- st_pass();
-
- /* ---- duplicate standard signal is coalesced ---- */
- st_begin("signal: duplicate standard signal coalesced");
- {
- curr->pending.signal = sigset_t{};
-
- send_signal(SIGUSR1, nullptr, curr);
- ST_ASSERT(curr->pending.signal.has(SIGUSR1));
-
- int ret = send_signal(SIGUSR1, nullptr, curr);
- ST_ASSERT_EQ(ret, 0);
-
- flush_pending(curr);
- }
- st_pass();
-
- /* ---- sigset_t: add/del/has ---- */
- st_begin("signal: sigset_t add/del/has");
- {
- sigset_t s{};
- ST_ASSERT(!s.has(SIGUSR1));
- s.add(SIGUSR1);
- ST_ASSERT(s.has(SIGUSR1));
- s.del(SIGUSR1);
- ST_ASSERT(!s.has(SIGUSR1));
- }
- st_pass();
-
- /* ---- sigset_t: bitwise operators ---- */
- st_begin("signal: sigset_t bitwise OR and AND");
- {
- sigset_t a{};
- a.add(SIGUSR1);
- sigset_t b{};
- b.add(SIGUSR2);
-
- sigset_t c = a | b;
- ST_ASSERT(c.has(SIGUSR1));
- ST_ASSERT(c.has(SIGUSR2));
-
- sigset_t d = c & a;
- ST_ASSERT(d.has(SIGUSR1));
- ST_ASSERT(!d.has(SIGUSR2));
- }
- st_pass();
-
- /* ---- blocked mask: direct manipulation ---- */
- st_begin("signal: blocked mask direct manipulation");
- {
- sigset_t saved = curr->blocked;
- curr->blocked = sigset_t{};
-
- curr->blocked.add(SIGUSR1);
- curr->blocked.add(SIGUSR2);
- ST_ASSERT(curr->blocked.has(SIGUSR1));
- ST_ASSERT(curr->blocked.has(SIGUSR2));
-
- curr->blocked.del(SIGUSR1);
- ST_ASSERT(!curr->blocked.has(SIGUSR1));
- ST_ASSERT(curr->blocked.has(SIGUSR2));
-
- curr->blocked = saved;
- }
- st_pass();
-
- /* ---- sighand action table ---- */
- st_begin("signal: sighand action table defaults to SIG_DFL");
- {
- /* All handlers should default to SIG_DFL (0) */
- kernel_sigaction* ka = &curr->sighand->action[SIGUSR1 - 1];
- ST_ASSERT_EQ(ka->sa_handler, (uint64)SIG_DFL);
-
- /* Set a handler and verify */
- ka->sa_handler = 0x12345678ULL;
- ST_ASSERT_EQ(ka->sa_handler, (uint64)0x12345678ULL);
-
- /* Reset */
- ka->sa_handler = SIG_DFL;
- }
- st_pass();
-
- /* ---- sig_kernel_only rejects SIGKILL/SIGSTOP handlers ---- */
- st_begin("signal: SIGKILL/SIGSTOP are kernel-only");
- {
- ST_ASSERT(sig_kernel_only(SIGKILL));
- ST_ASSERT(sig_kernel_only(SIGSTOP));
- ST_ASSERT(!sig_kernel_only(SIGUSR1));
- ST_ASSERT(!sig_kernel_only(SIGINT));
- }
- st_pass();
-
- /* ---- sig_default_action correctness ---- */
- st_begin("signal: sig_default_action returns correct actions");
- {
- ST_ASSERT_EQ((int)sig_default_action(SIGCHLD), (int)SIG_ACT_IGN);
- ST_ASSERT_EQ((int)sig_default_action(SIGKILL), (int)SIG_ACT_TERM);
- ST_ASSERT_EQ((int)sig_default_action(SIGSTOP), (int)SIG_ACT_STOP);
- ST_ASSERT_EQ((int)sig_default_action(SIGCONT), (int)SIG_ACT_CONT);
- ST_ASSERT_EQ((int)sig_default_action(SIGSEGV), (int)SIG_ACT_CORE);
- }
- st_pass();
-
- /* Clean up */
- flush_pending(curr);
- curr->blocked = sigset_t{};
-}
diff --git a/na/test/test_slab.cc b/na/test/test_slab.cc
deleted file mode 100644
index 8a448b4..0000000
--- a/na/test/test_slab.cc
+++ /dev/null
@@ -1,255 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot Self-Test: Slab Allocator (kmalloc/kfree)
- *
- * Tests:
- * 1. Small allocation (8 bytes) and free
- * 2. Multiple size classes
- * 3. No aliasing between consecutive allocs
- * 4. Alignment check (minimum 8-byte aligned)
- * 5. Bulk alloc/free stress
- * 6. operator new / operator delete
- * 7. Large allocation (> 2048, falls back to PMM)
- * 8. Zero-size and null-free edge cases
- */
-
-#include <lume/selftest.h>
-#include <lume/new.h>
-#include <lume/pmm.h>
-#include <lume/addr.h>
-
-/* Forward declarations — defined in slab.cc */
-void* kmalloc(uint32 size);
-void kfree(void* ptr);
-
-void selftest_slab()
-{
- st_begin("slab: small alloc (8 bytes)");
- {
- void* p = kmalloc(8);
- ST_ASSERT(p != nullptr);
- // Write and read back to verify the memory is usable
- *static_cast<uint64*>(p) = 0xDEADBEEFCAFEBABEULL;
- ST_ASSERT_EQ(*static_cast<uint64*>(p), 0xDEADBEEFCAFEBABEULL);
- kfree(p);
- }
- st_pass();
-
- st_begin("slab: multiple size classes");
- {
- uint32 sizes[] = {8, 16, 32, 64, 128, 256, 512, 1024, 2048};
- void* ptrs[9];
- for (int i = 0; i < 9; i++) {
- ptrs[i] = kmalloc(sizes[i]);
- ST_ASSERT(ptrs[i] != nullptr);
- // Write a marker
- *static_cast<uint8*>(ptrs[i]) = static_cast<uint8>(i);
- }
- // Verify markers (no cross-contamination)
- for (int i = 0; i < 9; i++) {
- ST_ASSERT_EQ(*static_cast<uint8*>(ptrs[i]), static_cast<uint8>(i));
- }
- for (int i = 0; i < 9; i++) {
- kfree(ptrs[i]);
- }
- }
- st_pass();
-
- st_begin("slab: no aliasing");
- {
- void* a = kmalloc(32);
- void* b = kmalloc(32);
- ST_ASSERT(a != nullptr);
- ST_ASSERT(b != nullptr);
- ST_ASSERT_NE(a, b);
- kfree(b);
- kfree(a);
- }
- st_pass();
-
- st_begin("slab: alignment (8-byte minimum)");
- {
- for (int i = 0; i < 16; i++) {
- void* p = kmalloc(7); // odd size, should still be 8-aligned
- ST_ASSERT(p != nullptr);
- ST_ASSERT_EQ(reinterpret_cast<uint64>(p) % 8, 0ULL);
- kfree(p);
- }
- }
- st_pass();
-
- st_begin("slab: bulk alloc/free stress (128 x 64B)");
- {
- constexpr int N = 128;
- void* ptrs[N];
- for (int i = 0; i < N; i++) {
- ptrs[i] = kmalloc(64);
- ST_ASSERT(ptrs[i] != nullptr);
- }
- // Free in reverse order
- for (int i = N - 1; i >= 0; i--) {
- kfree(ptrs[i]);
- }
- }
- st_pass();
-
- st_begin("slab: operator new/delete");
- {
- struct TestObj {
- uint64 a;
- uint64 b;
- };
- TestObj* obj = new TestObj{42, 99};
- ST_ASSERT(obj != nullptr);
- ST_ASSERT_EQ(obj->a, 42ULL);
- ST_ASSERT_EQ(obj->b, 99ULL);
- delete obj;
- }
- st_pass();
-
- st_begin("slab: large alloc (4096 bytes, PMM fallback)");
- {
- void* p = kmalloc(4096);
- ST_ASSERT(p != nullptr);
- ST_ASSERT_EQ(reinterpret_cast<uint64>(p) % kPageSize, 0ULL);
- kfree(p);
- }
- st_pass();
-
- st_begin("slab: edge cases (zero size, null free)");
- {
- void* p = kmalloc(0);
- ST_ASSERT(p == nullptr);
- kfree(nullptr); // should not crash
- }
- st_pass();
- st_begin("slab: LIFO freelist reallocation (same address)");
- {
- void* a = kmalloc(32);
- kfree(a);
- void* b = kmalloc(32);
- ST_ASSERT_EQ(a, b);
- kfree(b);
- }
- st_pass();
-
- st_begin("slab: boundary conditions (2048 bytes size class)");
- {
- // 2048 bytes should still use slab (2 objects per page)
- // We allocate 3 to guarantee at least two fall in the exact same slab page
- void* p1 = kmalloc(2048);
- void* p2 = kmalloc(2048);
- void* p3 = kmalloc(2048);
-
- auto diff = [](void* a, void* b) -> uint64 {
- uint64 ua = reinterpret_cast<uint64>(a);
- uint64 ub = reinterpret_cast<uint64>(b);
- return ua > ub ? ua - ub : ub - ua;
- };
-
- // At least one adjacent pair MUST be exactly separated by 2048 bytes
- bool found_2048 = (diff(p1, p2) == 2048ULL) || (diff(p2, p3) == 2048ULL);
- ST_ASSERT(found_2048);
-
- kfree(p1);
- kfree(p2);
- kfree(p3);
- }
- st_pass();
-
- st_begin("slab: cross-page allocation (> 1 page equivalent objects)");
- {
- // Allocating more than one page worth of 32-byte objects (4096 / 32 = 128)
- constexpr int NUM = 150;
- void* ptrs[NUM];
- for (int i=0; i<NUM; i++) {
- ptrs[i] = kmalloc(32);
- ST_ASSERT(ptrs[i] != nullptr);
- }
- for (int i=0; i<NUM; i++) {
- kfree(ptrs[i]);
- }
- }
- st_pass();
-
- st_begin("slab: empty page reclamation (verifiable by page availability)");
- {
- // Allocate 128-byte objects until we hit the start of a completely new page.
- void* base_ptr = nullptr;
- void* drain[64];
- int drain_count = 0;
-
- while (true) {
- void* p = kmalloc(128);
- // Because 128 divides 4096 evenly, a new page starts at an exact multiple of kPageSize.
- if (reinterpret_cast<uint64>(p) % kPageSize == 0) {
- base_ptr = p;
- break;
- }
- drain[drain_count++] = p;
- }
-
- // base_ptr is the very first object in a fresh slab page!
- // The page holds exactly 32 objects (4096 / 128 = 32). We already hold 1 (base_ptr).
- void* ptrs[32];
- ptrs[0] = base_ptr;
- for (int i = 1; i < 32; i++) {
- ptrs[i] = kmalloc(128);
- }
-
- // At this point, we completely own all 32 objects of this slab page.
- uint64 page_pa = va_to_pa(virt_addr(reinterpret_cast<uint64>(base_ptr))).raw;
- Page* page = pa_to_page(phys_addr(page_pa));
-
- ST_ASSERT(page->type == PageType::Slab);
- ST_ASSERT(page->slab.obj_count == 32);
-
- // Free all items in this isolated slab page
- for (int i = 0; i < 32; i++) {
- kfree(ptrs[i]);
- }
-
- // The page MUST have been returned to the PMM (either global Free or Per-CPU Cached).
- ST_ASSERT(page->type == PageType::Free || page->type == PageType::PcpCached);
-
- // Clean up the drain array
- for (int i = 0; i < drain_count; i++) {
- kfree(drain[i]);
- }
- }
- st_pass();
-
- st_begin("slab: continuous operator new/delete memory leak loop");
- {
- struct BigObj { uint8 data[512]; };
- BigObj* ptr = nullptr;
- // 10K allocations to ensure no memory leak causes OOM
- for (int i = 0; i < 10000; i++) {
- ptr = new BigObj;
- ST_ASSERT(ptr != nullptr);
- delete ptr;
- }
- }
- st_pass();
-
- st_begin("slab: allocation pressure (mixed sizes)");
- {
- constexpr int MAX_ITEMS = 100;
- void* ptrs[MAX_ITEMS];
- uint32 sizes[MAX_ITEMS];
-
- for (int i = 0; i < MAX_ITEMS; i++) {
- sizes[i] = (i * 17) % 2000 + 8; // Pseudo-random sizes between 8 and 2008
- ptrs[i] = kmalloc(sizes[i]);
- ST_ASSERT(ptrs[i] != nullptr);
- ST_ASSERT(reinterpret_cast<uintptr>(ptrs[i]) % 8 == 0);
- }
-
- for (int i = 0; i < MAX_ITEMS; i++) {
- kfree(ptrs[i]);
- }
- }
- st_pass();
-}
diff --git a/na/test/test_smp.cc b/na/test/test_smp.cc
deleted file mode 100644
index f934526..0000000
--- a/na/test/test_smp.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_smp.cc — Post-SMP boot tests
- *
- * These tests run after all APs are online. They verify:
- * 1. IPI delivery — BSP sends IPI to each AP, AP increments counter.
- * 2. Task migration — a task moves from one runqueue to another.
- * 3. TLB shootdown IPI — cross-CPU flush.
- *
- * Called from the init thread (PID 1) context, NOT from the pre-SMP
- * self-test suite.
- */
-
-#include <lume/selftest.h>
-#include <lume/smp.h>
-#include <lume/sched.h>
-#include <lume/atomic.h>
-#include <lume/config.h>
-#include <arch/cpu.h>
-
-/* Shared counter incremented by APs when they receive an IPI */
-static lume::atomic<uint32> g_ipi_ack_count{0};
-
-/* ------------------------------------------------------------------ */
-/* IPI delivery test */
-/* ------------------------------------------------------------------ */
-
-void selftest_ipi_delivery()
-{
- st_begin("smp: IPI delivery to all APs");
-
- /* Reset counter */
- g_ipi_ack_count.store(0, __ATOMIC_RELAXED);
-
- uint32 bsp = static_cast<uint32>(arch::cpu::id());
- uint32 num_cpus = 4; /* TODO: derive from FDT */
-
- /* Build mask of all APs */
- uint64 ap_mask = 0;
- uint32 expected = 0;
- for (uint32 i = 0; i < num_cpus; i++) {
- if (i == bsp) continue;
- ap_mask |= (1ULL << i);
- expected++;
- }
-
- /* Send reschedule IPI to all APs */
- smp_send_ipi(ap_mask, IPI_RESCHEDULE);
-
- /* Wait a short while for APs to process the interrupt.
- * APs are in wfi (idle loop), so they'll wake immediately
- * on interrupt delivery. We just need to give them time
- * to run the handler. */
- for (volatile int i = 0; i < 100000; i++) {
- /* spin-wait */
- }
-
- /* We can't directly check that IPI was received because
- * IPI_RESCHEDULE just sets need_resched — no ack.
- * Instead verify no crash occurred and the IPI infrastructure
- * didn't deadlock. This is a liveness test. */
-
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* IPI halt test (send to self, skip) */
-/* ------------------------------------------------------------------ */
-
-void selftest_ipi_self_skip()
-{
- st_begin("smp: IPI reason bitmask accumulation");
-
- uint32 cpu = static_cast<uint32>(arch::cpu::id());
-
- /* Manually set multiple reasons atomically */
- g_ipi_data[cpu].pending.store(0, __ATOMIC_RELAXED);
- g_ipi_data[cpu].pending.fetch_or(IPI_RESCHEDULE, __ATOMIC_RELEASE);
- g_ipi_data[cpu].pending.fetch_or(IPI_TLB_SHOOTDOWN, __ATOMIC_RELEASE);
-
- uint32 val = g_ipi_data[cpu].pending.load(__ATOMIC_ACQUIRE);
- ST_ASSERT_EQ(val, IPI_RESCHEDULE | IPI_TLB_SHOOTDOWN);
-
- /* Clean up */
- g_ipi_data[cpu].pending.store(0, __ATOMIC_RELAXED);
-
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* TLB shootdown IPI test */
-/* ------------------------------------------------------------------ */
-
-void selftest_tlb_shootdown()
-{
- st_begin("smp: TLB shootdown IPI (cross-CPU flush)");
-
- uint32 bsp = static_cast<uint32>(arch::cpu::id());
- uint32 num_cpus = 4;
-
- /* Build AP mask */
- uint64 ap_mask = 0;
- for (uint32 i = 0; i < num_cpus; i++) {
- if (i == bsp) continue;
- ap_mask |= (1ULL << i);
- }
-
- /* Set TLB range on each AP's IpiData */
- for (uint32 i = 0; i < num_cpus; i++) {
- if (i == bsp) continue;
- g_ipi_data[i].tlb_start = 0;
- g_ipi_data[i].tlb_end = 0; /* 0,0 = full flush */
- }
-
- /* Send TLB shootdown */
- smp_send_ipi(ap_mask, IPI_TLB_SHOOTDOWN);
-
- /* Wait for APs to process */
- for (volatile int i = 0; i < 100000; i++) {}
-
- /* If we got here without panic, TLB shootdown succeeded */
- st_pass();
-}
-
-/* ------------------------------------------------------------------ */
-/* Public entry point */
-/* ------------------------------------------------------------------ */
-
-void selftest_smp()
-{
- selftest_ipi_delivery();
- selftest_ipi_self_skip();
- selftest_tlb_shootdown();
-}
diff --git a/na/test/test_spinlock.cc b/na/test/test_spinlock.cc
deleted file mode 100644
index be794a0..0000000
--- a/na/test/test_spinlock.cc
+++ /dev/null
@@ -1,135 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot Self-Test: Spinlock
- *
- * Tests:
- * 1. Basic acquire/release
- * 2. LockGuard RAII acquire/release
- * 3. Nested locking (two different locks)
- * 4. Interrupt state preservation across lock/unlock
- * 5. is_held_by_current_cpu correctness
- *
- * Note: We cannot test multi-core contention in BSP-only self-test.
- * That requires AP participation (future Phase 2 test).
- */
-
-#include <lume/selftest.h>
-#include <kernel/sync/spinlock.h>
-#include <kernel/sync/cpu_state.h>
-#include <arch/cpu.h>
-
-void selftest_spinlock()
-{
- st_begin("spinlock: basic acquire/release");
- {
- Spinlock lock;
- lock.init("test_basic");
-
- ST_ASSERT(!lock.is_held_by_current_cpu());
-
- lock.acquire();
- ST_ASSERT(lock.is_held_by_current_cpu());
-
- lock.release();
- ST_ASSERT(!lock.is_held_by_current_cpu());
- }
- st_pass();
-
- st_begin("spinlock: LockGuard RAII");
- {
- Spinlock lock;
- lock.init("test_guard");
-
- {
- LockGuard guard(lock);
- ST_ASSERT(lock.is_held_by_current_cpu());
- }
- // After scope exit, lock should be released
- ST_ASSERT(!lock.is_held_by_current_cpu());
- }
- st_pass();
-
- st_begin("spinlock: nested locking (two locks)");
- {
- Spinlock lock_a, lock_b;
- lock_a.init("test_nest_a");
- lock_b.init("test_nest_b");
-
- lock_a.acquire();
- ST_ASSERT(lock_a.is_held_by_current_cpu());
-
- lock_b.acquire();
- ST_ASSERT(lock_b.is_held_by_current_cpu());
-
- lock_b.release();
- ST_ASSERT(!lock_b.is_held_by_current_cpu());
- ST_ASSERT(lock_a.is_held_by_current_cpu()); // A still held
-
- lock_a.release();
- ST_ASSERT(!lock_a.is_held_by_current_cpu());
- }
- st_pass();
-
- st_begin("spinlock: interrupt state preservation");
- {
- Spinlock lock;
- lock.init("test_intr");
-
- // Interrupts are currently off (BSP init period), but let's
- // verify the lock doesn't corrupt the state.
- bool before = arch::cpu::intr_enabled();
-
- lock.acquire();
- // Inside lock, interrupts must be disabled
- ST_ASSERT(!arch::cpu::intr_enabled());
- lock.release();
-
- // After release, interrupt state should be restored
- ST_ASSERT_EQ(arch::cpu::intr_enabled(), before);
- }
- st_pass();
- st_begin("spinlock: deep nesting tracking (> 2)");
- {
- Spinlock l1, l2, l3;
- l1.init("l1");
- l2.init("l2");
- l3.init("l3");
-
- bool before = arch::cpu::intr_enabled();
-
- l1.acquire();
- l2.acquire();
- l3.acquire();
-
- ST_ASSERT(!arch::cpu::intr_enabled());
-
- l3.release();
- ST_ASSERT(!arch::cpu::intr_enabled());
-
- l2.release();
- ST_ASSERT(!arch::cpu::intr_enabled());
-
- l1.release();
- ST_ASSERT_EQ(arch::cpu::intr_enabled(), before);
- }
- st_pass();
-
- st_begin("spinlock: interrupt leak detection logic (lock_depth metadata)");
- {
- Spinlock l;
- l.init("l");
-
- auto& state = sync::g_cpu_sync_states[arch::cpu::id()];
- int initial_depth = state.lock_depth;
-
- l.acquire();
- // The lock depth should strictly increase
- ST_ASSERT_EQ(state.lock_depth, initial_depth + 1);
- l.release();
-
- ST_ASSERT_EQ(state.lock_depth, initial_depth);
- }
- st_pass();
-}
diff --git a/na/test/test_sys_fs_extra.cc b/na/test/test_sys_fs_extra.cc
deleted file mode 100644
index 705ce63..0000000
--- a/na/test/test_sys_fs_extra.cc
+++ /dev/null
@@ -1,238 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_sys_fs_extra.cc — Self-tests for additional filesystem syscalls
- *
- * Tests: dup3, chdir/getcwd, writev, ioctl (stub), unlinkat (stub).
- * Complements test_vfs_io.cc which covers the core read/write/open/close.
- */
-
-#include <lume/selftest.h>
-#include <lume/errno.h>
-#include <lume/file.h>
-#include <lume/fs.h>
-#include <lume/fs_types.h>
-#include <lume/task.h>
-
-/* Syscall entry points */
-int64 sys_openat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_close(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_read(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_write(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_dup3(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_chdir(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getcwd(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_writev(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_ioctl(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_unlinkat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_mkdirat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_lseek(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* ================================================================== */
-/* Helpers */
-/* ================================================================== */
-
-static int64 open_dev(const char* path, int flags)
-{
- return sys_openat(
- static_cast<uint64>(-100),
- reinterpret_cast<uint64>(path),
- static_cast<uint64>(flags), 0, 0, 0);
-}
-
-static int64 do_close(int64 fd)
-{
- return sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
-}
-
-static int64 do_read(int64 fd, void* buf, uint64 count)
-{
- return sys_read(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf), count, 0, 0, 0);
-}
-
-
-/* iovec must match sys_fs.cc definition */
-struct test_iovec {
- void* iov_base;
- uint64 iov_len;
-};
-
-/* ================================================================== */
-/* Tests */
-/* ================================================================== */
-
-void selftest_sys_fs_extra()
-{
- /* ---- dup3: duplicate fd to specific target ---- */
- st_begin("sys_fs: dup3 duplicates to specific fd");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- /* dup3 to fd 50 */
- int64 fd2 = sys_dup3(static_cast<uint64>(fd), 50, 0, 0, 0, 0);
- ST_ASSERT_EQ(fd2, (int64)50);
-
- /* Both should work */
- char buf[4];
- int64 r = do_read(fd2, buf, 4);
- ST_ASSERT_EQ(r, (int64)0); /* /dev/null → EOF */
-
- do_close(fd);
- do_close(fd2);
- }
- st_pass();
-
- /* ---- dup3: same fd returns -EINVAL ---- */
- st_begin("sys_fs: dup3 same fd returns -EINVAL");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- int64 ret = sys_dup3(static_cast<uint64>(fd),
- static_cast<uint64>(fd), 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-EINVAL);
-
- do_close(fd);
- }
- st_pass();
-
- /* ---- chdir to /dev, getcwd returns /dev ---- */
- st_begin("sys_fs: chdir then getcwd returns new dir");
- {
- /* Save cwd */
- char saved_cwd[64] = {};
- sys_getcwd(reinterpret_cast<uint64>(saved_cwd), 64, 0, 0, 0, 0);
-
- /* chdir to /dev */
- int64 ret = sys_chdir(
- reinterpret_cast<uint64>("/dev"), 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)0);
-
- /* getcwd should return /dev */
- char buf[64] = {};
- int64 len = sys_getcwd(
- reinterpret_cast<uint64>(buf), 64, 0, 0, 0, 0);
- ST_ASSERT(len > 0);
- ST_ASSERT_EQ(buf[0], '/');
- ST_ASSERT_EQ(buf[1], 'd');
- ST_ASSERT_EQ(buf[2], 'e');
- ST_ASSERT_EQ(buf[3], 'v');
-
- /* Restore cwd to / */
- sys_chdir(reinterpret_cast<uint64>("/"), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- chdir to nonexistent returns -ENOENT ---- */
- st_begin("sys_fs: chdir nonexistent returns -ENOENT");
- {
- int64 ret = sys_chdir(
- reinterpret_cast<uint64>("/no_such_dir"), 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-ENOENT);
- }
- st_pass();
-
- /* ---- chdir to file returns -ENOTDIR ---- */
- st_begin("sys_fs: chdir to file returns -ENOTDIR");
- {
- int64 ret = sys_chdir(
- reinterpret_cast<uint64>("/dev/null"), 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-ENOTDIR);
- }
- st_pass();
-
- /* ---- writev: scatter write to file ---- */
- st_begin("sys_fs: writev scatter write");
- {
- /* Create a file, write with writev, read back */
- int64 fd = sys_openat(
- static_cast<uint64>(-100),
- reinterpret_cast<uint64>("/writev_test"),
- O_CREAT | O_RDWR, 0644, 0, 0);
- ST_ASSERT(fd >= 0);
-
- char a[] = "AAA";
- char b[] = "BBB";
- test_iovec iov[2];
- iov[0].iov_base = a;
- iov[0].iov_len = 3;
- iov[1].iov_base = b;
- iov[1].iov_len = 3;
-
- int64 w = sys_writev(
- static_cast<uint64>(fd),
- reinterpret_cast<uint64>(iov), 2, 0, 0, 0);
- ST_ASSERT_EQ(w, (int64)6);
-
- /* Seek back to start and read */
- sys_lseek(static_cast<uint64>(fd), 0, 0 /* SEEK_SET */, 0, 0, 0);
-
- char buf[8] = {};
- int64 r = do_read(fd, buf, 8);
- ST_ASSERT_EQ(r, (int64)6);
- ST_ASSERT_EQ(buf[0], 'A');
- ST_ASSERT_EQ(buf[3], 'B');
-
- do_close(fd);
- }
- st_pass();
-
- /* ---- writev: bad fd returns -EBADF ---- */
- st_begin("sys_fs: writev bad fd returns -EBADF");
- {
- test_iovec iov[1];
- char dummy[] = "x";
- iov[0].iov_base = dummy;
- iov[0].iov_len = 1;
-
- int64 ret = sys_writev(999,
- reinterpret_cast<uint64>(iov), 1, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-EBADF);
- }
- st_pass();
-
- /* ---- ioctl stub returns -ENOTTY ---- */
- st_begin("sys_fs: ioctl stub returns -ENOTTY");
- {
- int64 fd = open_dev("/dev/null", O_RDONLY);
- ST_ASSERT(fd >= 0);
-
- int64 ret = sys_ioctl(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-ENOTTY);
-
- do_close(fd);
- }
- st_pass();
-
- /* ---- unlinkat stub returns -ENOSYS ---- */
- st_begin("sys_fs: unlinkat stub returns -ENOSYS");
- {
- int64 ret = sys_unlinkat(
- static_cast<uint64>(-100),
- reinterpret_cast<uint64>("/some_file"),
- 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-ENOSYS);
- }
- st_pass();
-
- /* ---- getcwd: null buf returns -EINVAL ---- */
- st_begin("sys_fs: getcwd null buf returns -EINVAL");
- {
- int64 ret = sys_getcwd(0, 64, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-EINVAL);
- }
- st_pass();
-
- /* ---- getcwd: zero size returns -EINVAL ---- */
- st_begin("sys_fs: getcwd zero size returns -EINVAL");
- {
- char buf[4];
- int64 ret = sys_getcwd(
- reinterpret_cast<uint64>(buf), 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-EINVAL);
- }
- st_pass();
-}
diff --git a/na/test/test_sys_mm.cc b/na/test/test_sys_mm.cc
deleted file mode 100644
index 2c1a7dc..0000000
--- a/na/test/test_sys_mm.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <lume/selftest.h>
-#include <lume/task.h>
-#include <lume/vmm.h>
-#include <lume/mman.h>
-#include <lume/errno.h>
-
-/* sys_mm.cc APIs */
-int64 sys_brk(uint64 new_brk, uint64, uint64, uint64, uint64, uint64);
-int64 sys_mmap(uint64 addr, uint64 length, uint64 prot,
- uint64 flags, uint64 fd, uint64 offset);
-int64 sys_munmap(uint64 addr, uint64 length, uint64, uint64, uint64, uint64);
-int64 sys_mprotect(uint64 addr, uint64 len, uint64 prot,
- uint64, uint64, uint64);
-
-void selftest_sys_mm()
-{
- st_begin("sys_mm: mmap invalid arguments");
- {
- TaskControlBlock* curr = current_task();
- VmSpace* old_mm = curr->mm;
-
- VmSpace* space = VmSpace::create();
- ST_ASSERT(space != nullptr);
- curr->mm = space;
-
- ST_ASSERT_EQ(sys_mmap(0, 0, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0), -EINVAL);
- ST_ASSERT_EQ(sys_mmap(0, 0x1000, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 1), -EINVAL);
- ST_ASSERT_EQ(sys_mmap(0, 0x1000, PROT_READ, 0, 0, 0), -EINVAL);
- ST_ASSERT_EQ(sys_mmap(0, 0x1000, PROT_READ, MAP_PRIVATE | MAP_SHARED, 0, 0), -EINVAL);
- ST_ASSERT_EQ(sys_mmap(0x123, 0x1000, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0), -EINVAL);
-
- curr->mm = old_mm;
- space->destroy();
- }
- st_pass();
-
- st_begin("sys_mm: mmap/munmap/mprotect basic flow");
- {
- TaskControlBlock* curr = current_task();
- VmSpace* old_mm = curr->mm;
-
- VmSpace* space = VmSpace::create();
- ST_ASSERT(space != nullptr);
- curr->mm = space;
-
- int64 addr = sys_mmap(0, 0x3000, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
- ST_ASSERT(addr > 0);
-
- ST_ASSERT_EQ(vmm_handle_page_fault(space, addr, VmFaultCause::Read), 0);
-
- int64 mp = sys_mprotect(addr + kPageSize, kPageSize, PROT_READ, 0, 0, 0);
- ST_ASSERT_EQ(mp, 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, addr + kPageSize, VmFaultCause::Write), -EFAULT);
-
- int64 mu = sys_munmap(addr, 0x3000, 0, 0, 0, 0);
- ST_ASSERT_EQ(mu, 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, addr, VmFaultCause::Read), -EFAULT);
-
- curr->mm = old_mm;
- space->destroy();
- }
- st_pass();
-
- st_begin("sys_mm: MAP_FIXED replaces existing mapping");
- {
- TaskControlBlock* curr = current_task();
- VmSpace* old_mm = curr->mm;
-
- VmSpace* space = VmSpace::create();
- ST_ASSERT(space != nullptr);
- curr->mm = space;
-
- uint64 base = 0x600000;
- ST_ASSERT_EQ(sys_mmap(base, 0x2000, PROT_READ,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0),
- static_cast<int64>(base));
-
- ST_ASSERT_EQ(sys_mmap(base, 0x1000, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0),
- static_cast<int64>(base));
-
- ST_ASSERT_EQ(vmm_handle_page_fault(space, base, VmFaultCause::Read), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, base, VmFaultCause::Write), 0);
-
- curr->mm = old_mm;
- space->destroy();
- }
- st_pass();
-
- st_begin("sys_mm: mmap hint fallback on overlap");
- {
- TaskControlBlock* curr = current_task();
- VmSpace* old_mm = curr->mm;
-
- VmSpace* space = VmSpace::create();
- ST_ASSERT(space != nullptr);
- curr->mm = space;
-
- uint64 base = 0x700000;
- ST_ASSERT_EQ(sys_mmap(base, 0x2000, PROT_READ,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0),
- static_cast<int64>(base));
-
- int64 hint = sys_mmap(base, 0x1000, PROT_READ,
- MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
- ST_ASSERT(hint > 0);
- ST_ASSERT(hint != static_cast<int64>(base));
- ST_ASSERT_EQ(vmm_handle_page_fault(space, hint, VmFaultCause::Read), 0);
-
- curr->mm = old_mm;
- space->destroy();
- }
- st_pass();
-
- st_begin("sys_mm: brk growth and shrink");
- {
- TaskControlBlock* curr = current_task();
- VmSpace* old_mm = curr->mm;
-
- VmSpace* space = VmSpace::create();
- ST_ASSERT(space != nullptr);
- curr->mm = space;
-
- int64 brk0 = sys_brk(0x400000, 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(brk0, 0x400000);
-
- int64 brk1 = sys_brk(0x402000, 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(brk1, 0x402000);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x400000, VmFaultCause::Read), 0);
-
- int64 brk2 = sys_brk(0x400000, 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(brk2, 0x400000);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x401000, VmFaultCause::Read), -EFAULT);
-
- curr->mm = old_mm;
- space->destroy();
- }
- st_pass();
-
- st_begin("sys_mm: brk invalid shrink below start");
- {
- TaskControlBlock* curr = current_task();
- VmSpace* old_mm = curr->mm;
-
- VmSpace* space = VmSpace::create();
- ST_ASSERT(space != nullptr);
- curr->mm = space;
-
- ST_ASSERT_EQ(sys_brk(0x500000, 0, 0, 0, 0, 0), 0x500000);
- ST_ASSERT_EQ(sys_brk(0x4FF000, 0, 0, 0, 0, 0), -EINVAL);
-
- curr->mm = old_mm;
- space->destroy();
- }
- st_pass();
-
- st_begin("sys_mm: mprotect invalid range");
- {
- TaskControlBlock* curr = current_task();
- VmSpace* old_mm = curr->mm;
-
- VmSpace* space = VmSpace::create();
- ST_ASSERT(space != nullptr);
- curr->mm = space;
-
- ST_ASSERT_EQ(sys_mmap(0, 0x1000, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0) > 0, true);
- ST_ASSERT_EQ(sys_mprotect(0x123, 0x1000, PROT_READ, 0, 0, 0), -EINVAL);
- ST_ASSERT_EQ(sys_mprotect(0x900000, 0x1000, PROT_READ, 0, 0, 0), -ENOMEM);
-
- curr->mm = old_mm;
- space->destroy();
- }
- st_pass();
-}
diff --git a/na/test/test_sys_process.cc b/na/test/test_sys_process.cc
deleted file mode 100644
index 686bed6..0000000
--- a/na/test/test_sys_process.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_sys_process.cc — Self-tests for process-control syscalls
- *
- * Tests: getpid, gettid, getppid, getuid, getgid, set_tid_address,
- * sched_yield, uname.
- */
-
-#include <lume/selftest.h>
-#include <lume/errno.h>
-#include <lume/task.h>
-#include <lume/types.h>
-
-/* Syscall entry points */
-int64 sys_getpid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_gettid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getppid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getuid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getgid(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_set_tid_address(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_sched_yield_wrapper(uint64, uint64, uint64, uint64, uint64, uint64);
-
-void selftest_sys_process()
-{
- /* ---- getpid returns current tgid ---- */
- st_begin("sys_process: getpid returns valid pid");
- {
- int64 pid = sys_getpid(0, 0, 0, 0, 0, 0);
- /* Boot task pid is -1 but tgid may differ; just verify it returns */
- TaskControlBlock* curr = current_task();
- ST_ASSERT_EQ(pid, (int64)curr->tgid);
- }
- st_pass();
-
- /* ---- gettid returns current pid (tid) ---- */
- st_begin("sys_process: gettid returns current tid");
- {
- int64 tid = sys_gettid(0, 0, 0, 0, 0, 0);
- TaskControlBlock* curr = current_task();
- ST_ASSERT_EQ(tid, (int64)curr->pid);
- }
- st_pass();
-
- /* ---- getppid returns parent tgid ---- */
- st_begin("sys_process: getppid returns parent tgid");
- {
- int64 ppid = sys_getppid(0, 0, 0, 0, 0, 0);
- TaskControlBlock* curr = current_task();
- int64 expected = curr->parent ? (int64)curr->parent->tgid : 0;
- ST_ASSERT_EQ(ppid, expected);
- }
- st_pass();
-
- /* ---- getuid returns 0 (root) ---- */
- st_begin("sys_process: getuid returns 0 (root)");
- {
- int64 uid = sys_getuid(0, 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(uid, (int64)0);
- }
- st_pass();
-
- /* ---- getgid returns 0 (root) ---- */
- st_begin("sys_process: getgid returns 0 (root)");
- {
- int64 gid = sys_getgid(0, 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(gid, (int64)0);
- }
- st_pass();
-
- /* ---- set_tid_address stores pointer and returns tid ---- */
- st_begin("sys_process: set_tid_address returns tid");
- {
- uint64 dummy_addr = 0x12345678ULL;
- int64 tid = sys_set_tid_address(dummy_addr, 0, 0, 0, 0, 0);
- TaskControlBlock* curr = current_task();
- ST_ASSERT_EQ(tid, (int64)curr->pid);
- ST_ASSERT_EQ(curr->clear_child_tid, dummy_addr);
-
- /* Reset it to avoid side effects */
- curr->clear_child_tid = 0;
- }
- st_pass();
-
- /* ---- sched_yield returns 0 ---- */
- st_begin("sys_process: sched_yield returns 0");
- {
- int64 ret = sys_sched_yield_wrapper(0, 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)0);
- }
-}
-
diff --git a/na/test/test_trap.cc b/na/test/test_trap.cc
deleted file mode 100644
index c91948a..0000000
--- a/na/test/test_trap.cc
+++ /dev/null
@@ -1,197 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot self-tests for the Trap/IRQ subsystem.
- *
- * Tests MD→MI translation, IrqManager register/dispatch/unregister,
- * and stvec installation verification.
- */
-
-#include <lume/types.h>
-#include <lume/selftest.h>
-#include <lume/trap.h>
-#include <lume/irq.h>
-#include <lume/errno.h>
-#include <lume/console.h>
-#include <arch/trap.h>
-
-TrapCause trap_translate_scause(uint64 scause);
-
-/* ========================================================================
- * IrqManager test helpers
- * ======================================================================== */
-
-static volatile uint32 g_test_irq_fired = 0;
-static volatile void* g_test_irq_arg = nullptr;
-
-static void test_irq_handler(uint32 irq, void* arg)
-{
- g_test_irq_fired = irq;
- g_test_irq_arg = arg;
-}
-
-/* ========================================================================
- * Test cases
- * ======================================================================== */
-
-static bool test_stvec_installed() {
- uint64 vec = arch::trap::get_vector();
- return vec != 0;
-}
-
-static bool test_scause_exception_translation() {
- /* Verify all exception causes map correctly */
- bool ok = true;
- ok = ok && (trap_translate_scause(EXC_LOAD_PAGE_FAULT) == TrapCause::PageFaultLoad);
- ok = ok && (trap_translate_scause(EXC_STORE_PAGE_FAULT) == TrapCause::PageFaultStore);
- ok = ok && (trap_translate_scause(EXC_INST_PAGE_FAULT) == TrapCause::PageFaultExec);
- ok = ok && (trap_translate_scause(EXC_ECALL_FROM_U) == TrapCause::Syscall);
- ok = ok && (trap_translate_scause(EXC_ILLEGAL_INST) == TrapCause::IllegalInst);
- ok = ok && (trap_translate_scause(EXC_BREAKPOINT) == TrapCause::Breakpoint);
- ok = ok && (trap_translate_scause(EXC_LOAD_ACCESS_FAULT) == TrapCause::AccessFault);
- ok = ok && (trap_translate_scause(EXC_INST_MISALIGNED) == TrapCause::AlignFault);
- return ok;
-}
-
-static bool test_scause_interrupt_translation() {
- bool ok = true;
- ok = ok && (trap_translate_scause(SCAUSE_INTERRUPT | IRQ_S_TIMER) == TrapCause::IrqTimer);
- ok = ok && (trap_translate_scause(SCAUSE_INTERRUPT | IRQ_S_SOFTWARE) == TrapCause::IrqSoftware);
- ok = ok && (trap_translate_scause(SCAUSE_INTERRUPT | IRQ_S_EXTERNAL) == TrapCause::IrqExternal);
- ok = ok && (trap_translate_scause(SCAUSE_INTERRUPT | 99) == TrapCause::Unknown);
- return ok;
-}
-
-static bool test_scause_unknown() {
- /* ecall from S-mode should map to Unknown (we only handle U-mode ecall) */
- return trap_translate_scause(EXC_ECALL_FROM_S) == TrapCause::Unknown;
-}
-
-static bool test_irqmanager_register_dispatch() {
- IrqManager mgr;
- mgr.init();
-
- g_test_irq_fired = 0;
- g_test_irq_arg = nullptr;
-
- int dummy_arg = 42;
- int ret = mgr.register_handler(10, test_irq_handler, &dummy_arg);
- if (ret != 0) return false;
-
- /* Dispatch IRQ 10 — handler should be called */
- mgr.dispatch(10);
-
- if (g_test_irq_fired != 10) return false;
- if (g_test_irq_arg != &dummy_arg) return false;
-
- return true;
-}
-
-static bool test_irqmanager_unregister() {
- IrqManager mgr;
- mgr.init();
-
- g_test_irq_fired = 0;
-
- mgr.register_handler(5, test_irq_handler, nullptr);
- mgr.unregister_handler(5);
-
- /* Dispatch after unregister — handler should NOT be called */
- mgr.dispatch(5);
-
- return g_test_irq_fired == 0;
-}
-
-static bool test_irqmanager_double_register() {
- IrqManager mgr;
- mgr.init();
-
- int ret1 = mgr.register_handler(7, test_irq_handler, nullptr);
- int ret2 = mgr.register_handler(7, test_irq_handler, nullptr);
-
- return ret1 == 0 && ret2 == -EBUSY;
-}
-
-static bool test_irqmanager_invalid_irq() {
- IrqManager mgr;
- mgr.init();
-
- int ret = mgr.register_handler(999, test_irq_handler, nullptr);
- return ret == -EINVAL;
-}
-
-static bool test_irqmanager_has_handler() {
- IrqManager mgr;
- mgr.init();
-
- if (mgr.has_handler(10)) return false; /* not yet registered */
-
- mgr.register_handler(10, test_irq_handler, nullptr);
- if (!mgr.has_handler(10)) return false; /* now registered */
-
- mgr.unregister_handler(10);
- if (mgr.has_handler(10)) return false; /* unregistered */
-
- return true;
-}
-
-static bool test_irqmanager_spurious_dispatch() {
- IrqManager mgr;
- mgr.init();
-
- g_test_irq_fired = 0;
-
- /* Dispatch an unregistered IRQ — should not crash or fire */
- mgr.dispatch(0);
- mgr.dispatch(63);
-
- return g_test_irq_fired == 0;
-}
-
-/* ========================================================================
- * Test runner
- * ======================================================================== */
-
-void selftest_trap()
-{
- st_begin("trap: stvec is installed");
- ST_ASSERT(test_stvec_installed());
- st_pass();
-
- st_begin("trap: scause exception translation");
- ST_ASSERT(test_scause_exception_translation());
- st_pass();
-
- st_begin("trap: scause interrupt translation");
- ST_ASSERT(test_scause_interrupt_translation());
- st_pass();
-
- st_begin("trap: scause unknown (ecall from S)");
- ST_ASSERT(test_scause_unknown());
- st_pass();
-
- st_begin("trap: IrqManager register/dispatch");
- ST_ASSERT(test_irqmanager_register_dispatch());
- st_pass();
-
- st_begin("trap: IrqManager unregister");
- ST_ASSERT(test_irqmanager_unregister());
- st_pass();
-
- st_begin("trap: IrqManager double register");
- ST_ASSERT(test_irqmanager_double_register());
- st_pass();
-
- st_begin("trap: IrqManager invalid IRQ");
- ST_ASSERT(test_irqmanager_invalid_irq());
- st_pass();
-
- st_begin("trap: IrqManager has_handler");
- ST_ASSERT(test_irqmanager_has_handler());
- st_pass();
-
- st_begin("trap: IrqManager spurious dispatch");
- ST_ASSERT(test_irqmanager_spurious_dispatch());
- st_pass();
-}
diff --git a/na/test/test_uaccess.cc b/na/test/test_uaccess.cc
deleted file mode 100644
index c5a8c6c..0000000
--- a/na/test/test_uaccess.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <lume/selftest.h>
-#include <lume/uaccess.h>
-#include <lume/errno.h>
-
-#include <lume/vmm.h>
-#include <lume/pmm.h>
-#include <lume/tlb.h>
-#include <lume/mm_layout.h>
-#include <arch/pmap.h>
-
-void selftest_uaccess()
-{
- /* To test valid user addresses (< kKernelVaBase), we need a writable page mapped there.
- * We cannot use VmSpace::create() because it does not yet inherit kernel mappings,
- * so switching to it hangs. We also cannot map it with the U bit because S-mode
- * would need the SUM bit enabled.
- * So we manually allocate a physical page and map it into the CURRENT page table
- * at 0x100000 without the U bit, just for this test.
- */
- uint64 mock_user_addr = 0x100000;
-
- uint64 satp;
- asm volatile("csrr %0, satp" : "=r"(satp));
- uint64 root_pa = (satp & 0xFFFFFFFFFFFULL) << 12;
-
- Page* page = pmm_alloc_page();
- ST_ASSERT(page != nullptr);
- uint64 paddr = page_to_pa(page).raw;
-
- pmap::map(root_pa, mock_user_addr, paddr, pmap::PTE_R | pmap::PTE_W | pmap::PTE_V | pmap::PTE_A | pmap::PTE_D);
-
- /* Flush TLB to see the new mapping */
- tlb_shootdown_all();
-
- st_begin("uaccess: copy_to_user valid range");
- char kernel_buf[32] = "Hello User Space!";
- int ret = copy_to_user(mock_user_addr, kernel_buf, 18);
- ST_ASSERT_EQ(ret, 0);
- st_pass();
-
- st_begin("uaccess: copy_from_user valid range");
- char read_buf[32] = {0};
- /* We wrote "Hello User Space!" to mock_user_addr previously */
- ret = copy_from_user(read_buf, mock_user_addr, 18);
- ST_ASSERT_EQ(ret, 0);
- ST_ASSERT(read_buf[0] == 'H');
- ST_ASSERT(read_buf[1] == 'e');
- ST_ASSERT(read_buf[6] == 'U');
- st_pass();
-
- st_begin("uaccess: strncpy_from_user valid range");
- char str_buf[32] = {0};
- int64 len = strncpy_from_user(str_buf, mock_user_addr, 32);
- ST_ASSERT_EQ(len, 17); /* "Hello User Space!" is 17 chars long */
- ST_ASSERT(str_buf[0] == 'H');
- st_pass();
-
- st_begin("uaccess: strncpy_from_user max_len truncation");
- char trunc_buf[32] = {0};
- len = strncpy_from_user(trunc_buf, mock_user_addr, 6);
- ST_ASSERT_EQ(len, 5); /* Will copy 5 chars and NUL terminate */
- ST_ASSERT(trunc_buf[4] == 'o');
- ST_ASSERT(trunc_buf[5] == '\0');
- st_pass();
-
- st_begin("uaccess: copy_to_user invalid range (crosses boundary)");
- ret = copy_to_user(kKernelVaBase - 10, kernel_buf, 20);
- ST_ASSERT_EQ(ret, -EFAULT);
- st_pass();
-
- st_begin("uaccess: copy_to_user invalid range (entirely in kernel)");
- ret = copy_to_user(kKernelVaBase + 0x1000, kernel_buf, 20);
- ST_ASSERT_EQ(ret, -EFAULT);
- st_pass();
-
- st_begin("uaccess: copy_to_user invalid range (overflow)");
- ret = copy_to_user(0xFFFFFFFFFFFFFFF0ULL, kernel_buf, 0x100);
- ST_ASSERT_EQ(ret, -EFAULT);
- st_pass();
-
- st_begin("uaccess: copy_from_user invalid ranges");
- ret = copy_from_user(read_buf, kKernelVaBase - 5, 10);
- ST_ASSERT_EQ(ret, -EFAULT);
-
- ret = copy_from_user(read_buf, kKernelVaBase, 10);
- ST_ASSERT_EQ(ret, -EFAULT);
- st_pass();
-
- st_begin("uaccess: strncpy_from_user invalid range");
- uint64 invalid_addr = kKernelVaBase + 0x1000;
- len = strncpy_from_user(str_buf, invalid_addr, 10);
- ST_ASSERT_EQ(len, -EFAULT);
- st_pass();
-}
diff --git a/na/test/test_vfs_io.cc b/na/test/test_vfs_io.cc
deleted file mode 100644
index 3d3b125..0000000
--- a/na/test/test_vfs_io.cc
+++ /dev/null
@@ -1,345 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * test_vfs_io.cc — Self-tests for VFS file I/O via syscalls
- *
- * Tests openat, read, write, close, lseek, dup, fstat, getdents64,
- * getcwd, chdir, and mkdirat through the kernel syscall layer.
- */
-
-#include <lume/selftest.h>
-#include <lume/errno.h>
-#include <lume/file.h>
-#include <lume/fs.h>
-#include <lume/fs_types.h>
-#include <lume/task.h>
-#include <lume/fdtable.h>
-
-/* Syscall entry points */
-int64 sys_openat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_close(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_read(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_write(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_lseek(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_dup(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_dup3(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_fstat_wrapper(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getcwd(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_chdir(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_mkdirat(uint64, uint64, uint64, uint64, uint64, uint64);
-int64 sys_getdents64(uint64, uint64, uint64, uint64, uint64, uint64);
-
-/* Helper: open a file, returns fd or negative error */
-static int64 open_file(const char* path, int flags, uint32 mode)
-{
- return sys_openat(
- static_cast<uint64>(-100), /* AT_FDCWD */
- reinterpret_cast<uint64>(path),
- static_cast<uint64>(flags),
- static_cast<uint64>(mode), 0, 0);
-}
-
-void selftest_vfs_io()
-{
- /* ---- openat O_CREAT creates a file ---- */
- st_begin("vfs_io: openat O_CREAT creates file");
- {
- int64 fd = open_file("/vfs_test_file", O_CREAT | O_WRONLY, 0644);
- ST_ASSERT(fd >= 0);
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- openat existing file ---- */
- st_begin("vfs_io: openat existing file succeeds");
- {
- int64 fd = open_file("/vfs_test_file", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- openat nonexistent without O_CREAT returns -ENOENT ---- */
- st_begin("vfs_io: openat nonexistent returns -ENOENT");
- {
- int64 fd = open_file("/no_such_file_ever", O_RDONLY, 0);
- ST_ASSERT_EQ(fd, (int64)-ENOENT);
- }
- st_pass();
-
- /* ---- write then read ---- */
- st_begin("vfs_io: write then read back data");
- {
- /* Write */
- int64 fd = open_file("/vfs_rw_test", O_CREAT | O_WRONLY, 0644);
- ST_ASSERT(fd >= 0);
-
- const char* msg = "Hello VFS!";
- int64 written = sys_write(
- static_cast<uint64>(fd),
- reinterpret_cast<uint64>(msg),
- 10, 0, 0, 0);
- ST_ASSERT_EQ(written, (int64)10);
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
-
- /* Read */
- fd = open_file("/vfs_rw_test", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
-
- char buf[32] = {};
- int64 nread = sys_read(
- static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf),
- sizeof(buf), 0, 0, 0);
- ST_ASSERT_EQ(nread, (int64)10);
-
- bool match = true;
- for (int i = 0; i < 10; i++) {
- if (buf[i] != msg[i]) { match = false; break; }
- }
- ST_ASSERT(match);
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- read past EOF returns 0 ---- */
- st_begin("vfs_io: read past EOF returns 0");
- {
- int64 fd = open_file("/vfs_rw_test", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
-
- char buf[64];
- /* First read: 10 bytes */
- int64 n1 = sys_read(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf), 64, 0, 0, 0);
- ST_ASSERT_EQ(n1, (int64)10);
-
- /* Second read: should be 0 (EOF) */
- int64 n2 = sys_read(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf), 64, 0, 0, 0);
- ST_ASSERT_EQ(n2, (int64)0);
-
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- O_TRUNC clears file content ---- */
- st_begin("vfs_io: O_TRUNC clears file content");
- {
- int64 fd = open_file("/vfs_rw_test", O_WRONLY | O_TRUNC, 0);
- ST_ASSERT(fd >= 0);
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
-
- fd = open_file("/vfs_rw_test", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
-
- char buf[16];
- int64 n = sys_read(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf), 16, 0, 0, 0);
- ST_ASSERT_EQ(n, (int64)0); /* truncated = empty */
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- close invalid fd returns -EBADF ---- */
- st_begin("vfs_io: close bad fd returns -EBADF");
- {
- int64 ret = sys_close(999, 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-EBADF);
- }
- st_pass();
-
- /* ---- read/write on bad fd returns -EBADF ---- */
- st_begin("vfs_io: read/write bad fd returns -EBADF");
- {
- char buf[8];
- int64 r = sys_read(999, reinterpret_cast<uint64>(buf), 8, 0, 0, 0);
- ST_ASSERT_EQ(r, (int64)-EBADF);
-
- int64 w = sys_write(999, reinterpret_cast<uint64>(buf), 8, 0, 0, 0);
- ST_ASSERT_EQ(w, (int64)-EBADF);
- }
- st_pass();
-
- /* ---- lseek SEEK_SET / SEEK_CUR / SEEK_END ---- */
- st_begin("vfs_io: lseek SEEK_SET/CUR/END");
- {
- /* Write 20 bytes */
- int64 fd = open_file("/vfs_lseek_test", O_CREAT | O_RDWR, 0644);
- ST_ASSERT(fd >= 0);
-
- char data[20];
- for (int i = 0; i < 20; i++) data[i] = static_cast<char>('A' + i);
- sys_write(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(data), 20, 0, 0, 0);
-
- /* SEEK_SET to 5 */
- int64 pos = sys_lseek(static_cast<uint64>(fd), 5, SEEK_SET, 0, 0, 0);
- ST_ASSERT_EQ(pos, (int64)5);
-
- /* Read 1 byte, should be 'F' */
- char c = 0;
- sys_read(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(&c), 1, 0, 0, 0);
- ST_ASSERT_EQ(c, 'F');
-
- /* SEEK_CUR +3 → pos 9 */
- pos = sys_lseek(static_cast<uint64>(fd), 3, SEEK_CUR, 0, 0, 0);
- ST_ASSERT_EQ(pos, (int64)9);
-
- /* SEEK_END -1 → pos 19 */
- pos = sys_lseek(static_cast<uint64>(fd), -1, SEEK_END, 0, 0, 0);
- ST_ASSERT_EQ(pos, (int64)19);
-
- sys_read(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(&c), 1, 0, 0, 0);
- ST_ASSERT_EQ(c, 'T');
-
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- lseek invalid whence returns -EINVAL ---- */
- st_begin("vfs_io: lseek invalid whence");
- {
- int64 fd = open_file("/vfs_lseek_test", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
- int64 ret = sys_lseek(static_cast<uint64>(fd), 0, 99, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)-EINVAL);
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- dup: duplicate fd shares file position ---- */
- st_begin("vfs_io: dup shares file position");
- {
- int64 fd1 = open_file("/vfs_lseek_test", O_RDONLY, 0);
- ST_ASSERT(fd1 >= 0);
-
- int64 fd2 = sys_dup(static_cast<uint64>(fd1), 0, 0, 0, 0, 0);
- ST_ASSERT(fd2 >= 0);
- ST_ASSERT_NE(fd1, fd2);
-
- /* Both should work for reading */
- char c = 0;
- int64 n = sys_read(static_cast<uint64>(fd2),
- reinterpret_cast<uint64>(&c), 1, 0, 0, 0);
- ST_ASSERT_EQ(n, (int64)1);
-
- sys_close(static_cast<uint64>(fd1), 0, 0, 0, 0, 0);
- sys_close(static_cast<uint64>(fd2), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- fstat: basic stat ---- */
- st_begin("vfs_io: fstat returns valid kstat");
- {
- int64 fd = open_file("/vfs_lseek_test", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
-
- kstat st{};
- int64 ret = sys_fstat_wrapper(
- static_cast<uint64>(fd),
- reinterpret_cast<uint64>(&st), 0, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)0);
- ST_ASSERT(S_ISREG(st.st_mode));
- ST_ASSERT_EQ(st.st_size, (int64)20);
-
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- getcwd: returns "/" ---- */
- st_begin("vfs_io: getcwd returns /");
- {
- char buf[64] = {};
- int64 len = sys_getcwd(
- reinterpret_cast<uint64>(buf), 64, 0, 0, 0, 0);
- ST_ASSERT(len > 0);
- ST_ASSERT_EQ(buf[0], '/');
- }
- st_pass();
-
- /* ---- mkdirat: create directory ---- */
- st_begin("vfs_io: mkdirat creates directory");
- {
- int64 ret = sys_mkdirat(
- static_cast<uint64>(-100),
- reinterpret_cast<uint64>("/vfs_io_dir"),
- 0755, 0, 0, 0);
- ST_ASSERT_EQ(ret, (int64)0);
-
- /* Open it to confirm it's a directory */
- int64 fd = open_file("/vfs_io_dir", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
-
- kstat st{};
- sys_fstat_wrapper(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(&st), 0, 0, 0, 0);
- ST_ASSERT(S_ISDIR(st.st_mode));
-
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- getdents64: list root directory ---- */
- st_begin("vfs_io: getdents64 lists root children");
- {
- int64 fd = open_file("/", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
-
- char buf[512] = {};
- int64 n = sys_getdents64(
- static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf),
- 512, 0, 0, 0);
- ST_ASSERT(n > 0); /* At least "." and ".." */
-
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- write to O_RDONLY fd returns -EBADF ---- */
- st_begin("vfs_io: write to O_RDONLY returns -EBADF");
- {
- int64 fd = open_file("/vfs_rw_test", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
-
- char buf[] = "x";
- int64 w = sys_write(static_cast<uint64>(fd),
- reinterpret_cast<uint64>(buf), 1, 0, 0, 0);
- ST_ASSERT_EQ(w, (int64)-EBADF);
- sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- }
- st_pass();
-
- /* ---- double close returns -EBADF ---- */
- st_begin("vfs_io: double close returns -EBADF");
- {
- int64 fd = open_file("/vfs_rw_test", O_RDONLY, 0);
- ST_ASSERT(fd >= 0);
-
- int64 r1 = sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(r1, (int64)0);
-
- int64 r2 = sys_close(static_cast<uint64>(fd), 0, 0, 0, 0, 0);
- ST_ASSERT_EQ(r2, (int64)-EBADF);
- }
- st_pass();
-
- /* ---- fd reuse after close ---- */
- st_begin("vfs_io: fd reuse after close");
- {
- int64 fd1 = open_file("/vfs_rw_test", O_RDONLY, 0);
- ST_ASSERT(fd1 >= 0);
- sys_close(static_cast<uint64>(fd1), 0, 0, 0, 0, 0);
-
- int64 fd2 = open_file("/vfs_rw_test", O_RDONLY, 0);
- ST_ASSERT(fd2 >= 0);
- /* fd2 should reuse the slot of fd1 */
- ST_ASSERT(fd2 <= fd1);
- sys_close(static_cast<uint64>(fd2), 0, 0, 0, 0, 0);
- }
- st_pass();
-}
diff --git a/na/test/test_vma.cc b/na/test/test_vma.cc
deleted file mode 100644
index 5278914..0000000
--- a/na/test/test_vma.cc
+++ /dev/null
@@ -1,282 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <lume/selftest.h>
-#include <lume/vmm.h>
-#include <lume/addr.h>
-#include <lume/errno.h>
-#include <arch/pmap.h>
-
-void selftest_vma() {
- st_begin("vma: create and destroy VmSpace");
- {
- VmSpace* space = VmSpace::create();
- ST_ASSERT(space != nullptr);
- ST_ASSERT(space->root_pa != 0);
-
- // Add a few regions to ensure destruction works
- vmm_map_user(space, 0x1000, 0x1000, VM_READ);
- vmm_map_user(space, 0x3000, 0x1000, VM_READ | VM_WRITE);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: map user region and handle demand paging");
- {
- VmSpace* space = VmSpace::create();
-
- // Map 8KB (2 pages) at 0x400000 using MI-layer flags
- int ret = vmm_map_user(space, 0x400000, 0x2000, VM_READ | VM_WRITE);
- ST_ASSERT_EQ(ret, 0);
-
- // Fault in the first page
- ret = vmm_handle_page_fault(space, 0x400500, VmFaultCause::Read);
- ST_ASSERT_EQ(ret, 0);
-
- // Verify mapping in hardware page table
- uint64 pa = 0;
- bool mapped = pmap::lookup(space->root_pa, 0x400000, &pa);
- ST_ASSERT(mapped);
- ST_ASSERT_NE(pa, 0ULL);
-
- // Fault in the second page
- ret = vmm_handle_page_fault(space, 0x401FFF, VmFaultCause::Read);
- ST_ASSERT_EQ(ret, 0);
-
- // Should reject fault outside VMA with -EFAULT
- ret = vmm_handle_page_fault(space, 0x402000, VmFaultCause::Read);
- ST_ASSERT_EQ(ret, -EFAULT);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: unmap user region prevents page fault");
- {
- VmSpace* space = VmSpace::create();
- vmm_map_user(space, 0x10000, 0x2000, VM_READ);
-
- // Inside the region — should succeed
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x10500, VmFaultCause::Read), 0);
-
- // Unmap exactly
- vmm_unmap_user(space, 0x10000, 0x2000);
-
- // Should fail now with -EFAULT
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x10500, VmFaultCause::Read), -EFAULT);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: duplicate page fault on same page returns 0");
- {
- VmSpace* space = VmSpace::create();
- vmm_map_user(space, 0x200000, 0x1000, VM_READ | VM_WRITE);
-
- // First fault allocates the physical page
- int ret = vmm_handle_page_fault(space, 0x200000, VmFaultCause::Read);
- ST_ASSERT_EQ(ret, 0);
-
- // Second fault on the same page should hit "already mapped" path
- ret = vmm_handle_page_fault(space, 0x200100, VmFaultCause::Read);
- ST_ASSERT_EQ(ret, 0);
-
- // Verify both land on the same physical page
- uint64 pa1 = 0, pa2 = 0;
- pmap::lookup(space->root_pa, 0x200000, &pa1);
- pmap::lookup(space->root_pa, 0x200100, &pa2);
- // Same page, so PA for both addresses should be the same base page
- ST_ASSERT_EQ(pa1, pa2);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: vm_perm_to_pte flag translation correctness");
- {
- // Test each MI flag maps to correct hardware PTE bit
- uint64 pte_r = pmap::vm_perm_to_pte(VM_READ);
- ST_ASSERT(pte_r & pmap::PTE_R);
- ST_ASSERT(!(pte_r & pmap::PTE_W));
- ST_ASSERT(!(pte_r & pmap::PTE_X));
- ST_ASSERT(!(pte_r & pmap::PTE_U));
-
- uint64 pte_rw = pmap::vm_perm_to_pte(VM_READ | VM_WRITE);
- ST_ASSERT(pte_rw & pmap::PTE_R);
- ST_ASSERT(pte_rw & pmap::PTE_W);
-
- uint64 pte_rx = pmap::vm_perm_to_pte(VM_READ | VM_EXEC);
- ST_ASSERT(pte_rx & pmap::PTE_R);
- ST_ASSERT(pte_rx & pmap::PTE_X);
- ST_ASSERT(!(pte_rx & pmap::PTE_W));
-
- uint64 pte_u = pmap::vm_perm_to_pte(VM_READ | VM_USER);
- ST_ASSERT(pte_u & pmap::PTE_R);
- ST_ASSERT(pte_u & pmap::PTE_U);
-
- // All combined
- uint64 pte_all = pmap::vm_perm_to_pte(VM_READ | VM_WRITE | VM_EXEC | VM_USER);
- ST_ASSERT(pte_all & pmap::PTE_R);
- ST_ASSERT(pte_all & pmap::PTE_W);
- ST_ASSERT(pte_all & pmap::PTE_X);
- ST_ASSERT(pte_all & pmap::PTE_U);
- ST_ASSERT(pte_all & pmap::PTE_V); // Always valid
- }
- st_pass();
-
- st_begin("vma: unmap subset only removes contained VMAs");
- {
- VmSpace* space = VmSpace::create();
- // Map three separate regions
- vmm_map_user(space, 0x50000, 0x1000, VM_READ);
- vmm_map_user(space, 0x60000, 0x1000, VM_READ);
- vmm_map_user(space, 0x70000, 0x1000, VM_READ);
-
- // Unmap only the middle one (range covers 0x60000-0x61000)
- vmm_unmap_user(space, 0x60000, 0x1000);
-
- // First and third should still be reachable
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x50000, VmFaultCause::Read), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x70000, VmFaultCause::Read), 0);
-
- // Middle should fail
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x60000, VmFaultCause::Read), -EFAULT);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: null space returns -EINVAL");
- {
- ST_ASSERT_EQ(vmm_map_user(nullptr, 0x1000, 0x1000, VM_READ), -EINVAL);
- ST_ASSERT_EQ(vmm_unmap_user(nullptr, 0x1000, 0x1000), -EINVAL);
- ST_ASSERT_EQ(vmm_handle_page_fault(nullptr, 0x1000, VmFaultCause::Read), -EINVAL);
- }
- st_pass();
-
- st_begin("vma: clone and copy-on-write");
- {
- VmSpace* parent = VmSpace::create();
- vmm_map_user(parent, 0x80000, 0x1000, VM_READ | VM_WRITE);
-
- /* Trigger read fault in parent to allocate physical page */
- ST_ASSERT_EQ(vmm_handle_page_fault(parent, 0x80000, VmFaultCause::Read), 0);
- uint64 parent_pa_initial;
- ST_ASSERT(pmap::lookup(parent->root_pa, 0x80000, &parent_pa_initial));
-
- /* Clone the address space */
- VmSpace* child = vmm_clone(parent);
- ST_ASSERT(child != nullptr);
-
- /* Verify child has the same physical page mapped but COW */
- uint64 child_pa;
- ST_ASSERT(pmap::lookup(child->root_pa, 0x80000, &child_pa));
- ST_ASSERT_EQ(child_pa, parent_pa_initial);
- ST_ASSERT(pmap::is_cow(child->root_pa, 0x80000));
- ST_ASSERT(pmap::is_cow(parent->root_pa, 0x80000)); /* Parent also marked COW */
-
- /* Trigger Store Page Fault (cause=15) in child */
- ST_ASSERT_EQ(vmm_handle_page_fault(child, 0x80000, VmFaultCause::Write), 0);
-
- /* Child should now have a new physical page, no longer COW */
- uint64 child_pa_new;
- ST_ASSERT(pmap::lookup(child->root_pa, 0x80000, &child_pa_new));
- ST_ASSERT(child_pa_new != parent_pa_initial);
- ST_ASSERT(!pmap::is_cow(child->root_pa, 0x80000));
-
- /* Parent is still mapped to the old page, still COW because it hasn't written */
- uint64 parent_pa_current;
- ST_ASSERT(pmap::lookup(parent->root_pa, 0x80000, &parent_pa_current));
- ST_ASSERT_EQ(parent_pa_current, parent_pa_initial);
- ST_ASSERT(pmap::is_cow(parent->root_pa, 0x80000));
-
- /* Trigger Store Page Fault in parent */
- ST_ASSERT_EQ(vmm_handle_page_fault(parent, 0x80000, VmFaultCause::Write), 0);
- ST_ASSERT(!pmap::is_cow(parent->root_pa, 0x80000));
-
- child->destroy();
- parent->destroy();
- }
- st_pass();
-
- st_begin("vma: gap search finds next free range");
- {
- VmSpace* space = VmSpace::create();
- vmm_map_user(space, 0x1000, 0x3000, VM_READ);
-
- uint64 va = mm_find_free_va(space, 0x1000, kPageSize);
- ST_ASSERT_EQ(va, 0x4000ULL);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: reject overlapping mapping");
- {
- VmSpace* space = VmSpace::create();
- ST_ASSERT_EQ(vmm_map_user(space, 0x20000, 0x2000, VM_READ), 0);
- ST_ASSERT_EQ(vmm_map_user(space, 0x21000, 0x1000, VM_READ), -EINVAL);
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: partial unmap splits a single VMA");
- {
- VmSpace* space = VmSpace::create();
- ST_ASSERT_EQ(vmm_map_user(space, 0x80000, 0x3000, VM_READ | VM_WRITE), 0);
-
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x80000, VmFaultCause::Read), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x81000, VmFaultCause::Read), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x82000, VmFaultCause::Read), 0);
-
- ST_ASSERT_EQ(vmm_unmap_user(space, 0x81000, 0x1000), 0);
-
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x80000, VmFaultCause::Read), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x82000, VmFaultCause::Read), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x81000, VmFaultCause::Read), -EFAULT);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: mprotect read-only blocks write faults");
- {
- VmSpace* space = VmSpace::create();
- ST_ASSERT_EQ(vmm_map_user(space, 0x90000, 0x2000, VM_READ | VM_WRITE), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x90000, VmFaultCause::Read), 0);
-
- ST_ASSERT_EQ(vmm_protect_user(space, 0x90000, 0x1000, VM_READ), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x90000, VmFaultCause::Write), -EFAULT);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0x91000, VmFaultCause::Write), 0);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: PROT_NONE unmaps existing pages");
- {
- VmSpace* space = VmSpace::create();
- ST_ASSERT_EQ(vmm_map_user(space, 0xA0000, 0x1000, VM_READ | VM_WRITE), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0xA0000, VmFaultCause::Read), 0);
-
- ST_ASSERT_EQ(vmm_protect_user(space, 0xA0000, 0x1000, 0), 0);
- ST_ASSERT_EQ(vmm_handle_page_fault(space, 0xA0000, VmFaultCause::Read), -EFAULT);
-
- space->destroy();
- }
- st_pass();
-
- st_begin("vma: range covered check");
- {
- VmSpace* space = VmSpace::create();
- ST_ASSERT_EQ(vmm_map_user(space, 0xB0000, 0x2000, VM_READ), 0);
-
- ST_ASSERT(vma_range_covered(space, 0xB0000, 0xB1000));
- ST_ASSERT(!vma_range_covered(space, 0xB0000, 0xB3000));
-
- space->destroy();
- }
- st_pass();
-}
diff --git a/na/test/test_vmm.cc b/na/test/test_vmm.cc
deleted file mode 100644
index 635e3f3..0000000
--- a/na/test/test_vmm.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-/*
- * Boot Self-Test: VMM and Address Translation
- */
-
-#include <lume/selftest.h>
-#include <lume/klog.h>
-#include <lume/config.h>
-#include <lume/addr.h>
-#include <lume/vmm.h>
-#include <lume/pmm.h>
-#include <arch/config.h>
-
-namespace {
-constexpr uint64 kUartBase = 0x10000000ULL;
-constexpr uint64 kPageOffsetMask = kPageSize - 1;
-}
-
-void selftest_vmm() {
- st_begin("vmm: virtual/physical address translation");
- {
- PhysAddr pa = pmm_get_mem_base();
- VirtAddr va = pa_to_va(pa);
- ST_ASSERT_EQ(va.raw, arch::g_direct_map_base + pa.raw);
- ST_ASSERT_EQ(va_to_pa(va).raw, pa.raw);
-
- Pfn p = pa_to_pfn(pa);
- ST_ASSERT_EQ(p.raw, pa.raw / kPageSize);
- ST_ASSERT_EQ(pfn_to_pa(p).raw, pa.raw);
- }
- st_pass();
-
- st_begin("vmm: page alignment macros");
- {
- uint64 base = pmm_get_mem_base().raw;
- uint64 addr = base + kPageOffsetMask;
- ST_ASSERT_EQ(page_align_down(addr), base);
- ST_ASSERT_EQ(page_align_up(addr), base + kPageSize);
- ST_ASSERT_EQ(page_align_down(base), base);
- ST_ASSERT_EQ(page_align_up(base), base);
- }
- st_pass();
-
- st_begin("vmm: mapping kernel MMIO (driver API)");
- {
- /* Use UART MMIO address (0x10000000 on RISC-V virt).
- * Must NOT use pmm_get_mem_base() — that's PMP-protected firmware. */
- uint64 mmio_pa = kUartBase;
- vmm_map_kernel_mmio(mmio_pa, kPageSize);
-
- // Verify it doesn't crash and address is reachable in higher half
- uint64 va = pa_to_va(phys_addr(mmio_pa)).raw;
- ST_ASSERT(va >= arch::g_direct_map_base);
- }
- st_pass();
-}
diff --git a/na/test/test_waitqueue.cc b/na/test/test_waitqueue.cc
deleted file mode 100644
index 7f50d6b..0000000
--- a/na/test/test_waitqueue.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Copyright (C) 2026 Richard Qin */
-
-#include <lume/selftest.h>
-#include <lume/waitqueue.h>
-#include <lume/task.h>
-#include <lume/sched.h>
-
-static WaitQueue g_test_wq;
-static volatile bool g_thread_woken = false;
-static volatile bool g_thread_started = false;
-static volatile int g_wake_count = 0;
-
-extern TaskControlBlock* kthread_create(const char* name, void (*entry)(void*), void* arg, uint8 priority);
-
-static void waitqueue_test_thread(void*)
-{
- g_thread_started = true;
- g_test_wq.sleep();
- g_thread_woken = true;
-
- extern void task_exit(int code);
- task_exit(0);
-}
-
-static void waitqueue_test_thread_all(void*)
-{
- g_test_wq.sleep();
- __sync_fetch_and_add(&g_wake_count, 1);
-
- extern void task_exit(int code);
- task_exit(0);
-}
-
-void selftest_waitqueue()
-{
- st_begin("waitqueue: sleep and wake_one");
-
- g_test_wq.init("test_wq");
- g_thread_started = false;
- g_thread_woken = false;
-
- TaskControlBlock* child = kthread_create("test_wq_child", waitqueue_test_thread, nullptr, 0);
- ST_ASSERT(child != nullptr);
-
- sched_yield();
-
- ST_ASSERT(g_thread_started == true);
- ST_ASSERT(g_thread_woken == false);
- ST_ASSERT(!g_test_wq.is_empty());
-
- kprintf("[test_wq] waking child\n");
- bool woken = g_test_wq.wake_one();
- ST_ASSERT(woken == true);
- ST_ASSERT(g_test_wq.is_empty());
-
- kprintf("[test_wq] yielding to child again\n");
- sched_yield();
- kprintf("[test_wq] returned from yield 2\n");
-
- ST_ASSERT(g_thread_woken == true);
-
- st_pass();
-
- st_begin("waitqueue: wake_all");
- g_wake_count = 0;
- for (int i = 0; i < 3; i++) {
- kthread_create("test_wq_all", waitqueue_test_thread_all, nullptr, 0);
- }
-
- /* Give all 3 threads a chance to start and sleep */
- kprintf("[test_wq] yielding 1 for wake_all\n");
- sched_yield();
- sched_yield();
- sched_yield();
-
- ST_ASSERT(!g_test_wq.is_empty());
-
- int woken_count = g_test_wq.wake_all();
-
- ST_ASSERT_EQ(woken_count, 3);
- ST_ASSERT(g_test_wq.is_empty());
-
- /* Give all 3 threads a chance to wake up and run */
- sched_yield();
- sched_yield();
- sched_yield();
-
- ST_ASSERT_EQ(g_wake_count, 3);
- st_pass();
-}
diff --git a/na/usr/default_cpio_list b/na/usr/default_cpio_list
deleted file mode 100644
index 6721365..0000000
--- a/na/usr/default_cpio_list
+++ /dev/null
@@ -1,24 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Default initramfs source — minimal, no userland.
-#
-# Mirrors Linux's usr/default_cpio_list: just enough nodes for the kernel
-# to mount a root and panic cleanly on "no init found". Real workloads
-# override CONFIG_INITRAMFS_SOURCE.
-#
-# Format (matches gen_init_cpio):
-# dir <path> <mode> <uid> <gid>
-# nod <path> <mode> <uid> <gid> <c|b> <maj> <min>
-# file <path> <hostsrc> <mode> <uid> <gid>
-# slink <path> <target> <mode> <uid> <gid>
-# Lines starting with '#' are comments. Blank lines are ignored.
-
-dir /dev 0755 0 0
-nod /dev/console 0600 0 0 c 5 1
-nod /dev/null 0666 0 0 c 1 3
-nod /dev/zero 0666 0 0 c 1 5
-dir /proc 0755 0 0
-dir /sys 0755 0 0
-dir /tmp 1777 0 0
-dir /sbin 0755 0 0
-dir /bin 0755 0 0
-dir /etc 0755 0 0
diff --git a/na/usr/gen_init_cpio.c b/na/usr/gen_init_cpio.c
deleted file mode 100644
index b7296ed..0000000
--- a/na/usr/gen_init_cpio.c
+++ /dev/null
@@ -1,781 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <unistd.h>
-#include <time.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <ctype.h>
-#include <limits.h>
-
-/*
- * Original work by Jeff Garzik
- *
- * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
- * Hard link support by Luciano Rocha
- */
-
-#define xstr(s) #s
-#define str(s) xstr(s)
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define CPIO_HDR_LEN 110
-#define CPIO_TRAILER "TRAILER!!!"
-#define padlen(_off, _align) (((_align) - ((_off) & ((_align) - 1))) % (_align))
-
-/* zero-padding the filename field for data alignment is limited by PATH_MAX */
-static char padding[PATH_MAX];
-static unsigned int offset;
-static unsigned int ino = 721;
-static time_t default_mtime;
-static bool do_file_mtime;
-static bool do_csum = false;
-static int outfd = STDOUT_FILENO;
-static unsigned int dalign;
-
-struct file_handler {
- const char *type;
- int (*handler)(const char *line);
-};
-
-static int push_buf(const char *name, size_t name_len)
-{
- ssize_t len;
-
- len = write(outfd, name, name_len);
- if (len != name_len)
- return -1;
-
- offset += name_len;
- return 0;
-}
-
-static int push_pad(size_t padlen)
-{
- ssize_t len = 0;
-
- if (!padlen)
- return 0;
-
- if (padlen < sizeof(padding))
- len = write(outfd, padding, padlen);
- if (len != padlen)
- return -1;
-
- offset += padlen;
- return 0;
-}
-
-static int push_rest(const char *name, size_t name_len)
-{
- ssize_t len;
-
- len = write(outfd, name, name_len);
- if (len != name_len)
- return -1;
-
- offset += name_len;
-
- return push_pad(padlen(name_len + CPIO_HDR_LEN, 4));
-}
-
-static int cpio_trailer(void)
-{
- int len;
- unsigned int namesize = sizeof(CPIO_TRAILER);
-
- len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
- "%08X%08X%08X%08X%08X%08X%08X",
- do_csum ? "070702" : "070701", /* magic */
- 0, /* ino */
- 0, /* mode */
- (long) 0, /* uid */
- (long) 0, /* gid */
- 1, /* nlink */
- (long) 0, /* mtime */
- 0, /* filesize */
- 0, /* major */
- 0, /* minor */
- 0, /* rmajor */
- 0, /* rminor */
- namesize, /* namesize */
- 0); /* chksum */
- offset += len;
-
- if (len != CPIO_HDR_LEN ||
- push_rest(CPIO_TRAILER, namesize) < 0 ||
- push_pad(padlen(offset, 512)) < 0)
- return -1;
-
- if (fsync(outfd) < 0 && errno != EINVAL)
- return -1;
-
- return 0;
-}
-
-static int cpio_mkslink(const char *name, const char *target,
- unsigned int mode, uid_t uid, gid_t gid)
-{
- int len;
- unsigned int namesize, targetsize = strlen(target) + 1;
-
- if (name[0] == '/')
- name++;
- namesize = strlen(name) + 1;
-
- len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
- "%08X%08X%08X%08X%08X%08X%08X",
- do_csum ? "070702" : "070701", /* magic */
- ino++, /* ino */
- S_IFLNK | mode, /* mode */
- (long) uid, /* uid */
- (long) gid, /* gid */
- 1, /* nlink */
- (long) default_mtime, /* mtime */
- targetsize, /* filesize */
- 3, /* major */
- 1, /* minor */
- 0, /* rmajor */
- 0, /* rminor */
- namesize, /* namesize */
- 0); /* chksum */
- offset += len;
-
- if (len != CPIO_HDR_LEN ||
- push_buf(name, namesize) < 0 ||
- push_pad(padlen(offset, 4)) < 0 ||
- push_buf(target, targetsize) < 0 ||
- push_pad(padlen(offset, 4)) < 0)
- return -1;
-
- return 0;
-
-}
-
-static int cpio_mkslink_line(const char *line)
-{
- char name[PATH_MAX + 1];
- char target[PATH_MAX + 1];
- unsigned int mode;
- int uid;
- int gid;
- int rc = -1;
-
- if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
- fprintf(stderr, "Unrecognized dir format '%s'", line);
- goto fail;
- }
- rc = cpio_mkslink(name, target, mode, uid, gid);
- fail:
- return rc;
-}
-
-static int cpio_mkgeneric(const char *name, unsigned int mode,
- uid_t uid, gid_t gid)
-{
- int len;
- unsigned int namesize;
-
- if (name[0] == '/')
- name++;
- namesize = strlen(name) + 1;
-
- len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
- "%08X%08X%08X%08X%08X%08X%08X",
- do_csum ? "070702" : "070701", /* magic */
- ino++, /* ino */
- mode, /* mode */
- (long) uid, /* uid */
- (long) gid, /* gid */
- 2, /* nlink */
- (long) default_mtime, /* mtime */
- 0, /* filesize */
- 3, /* major */
- 1, /* minor */
- 0, /* rmajor */
- 0, /* rminor */
- namesize, /* namesize */
- 0); /* chksum */
- offset += len;
-
- if (len != CPIO_HDR_LEN ||
- push_rest(name, namesize) < 0)
- return -1;
-
- return 0;
-}
-
-enum generic_types {
- GT_DIR,
- GT_PIPE,
- GT_SOCK
-};
-
-struct generic_type {
- const char *type;
- mode_t mode;
-};
-
-static const struct generic_type generic_type_table[] = {
- [GT_DIR] = {
- .type = "dir",
- .mode = S_IFDIR
- },
- [GT_PIPE] = {
- .type = "pipe",
- .mode = S_IFIFO
- },
- [GT_SOCK] = {
- .type = "sock",
- .mode = S_IFSOCK
- }
-};
-
-static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
-{
- char name[PATH_MAX + 1];
- unsigned int mode;
- int uid;
- int gid;
- int rc = -1;
-
- if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
- fprintf(stderr, "Unrecognized %s format '%s'",
- line, generic_type_table[gt].type);
- goto fail;
- }
- mode |= generic_type_table[gt].mode;
- rc = cpio_mkgeneric(name, mode, uid, gid);
- fail:
- return rc;
-}
-
-static int cpio_mkdir_line(const char *line)
-{
- return cpio_mkgeneric_line(line, GT_DIR);
-}
-
-static int cpio_mkpipe_line(const char *line)
-{
- return cpio_mkgeneric_line(line, GT_PIPE);
-}
-
-static int cpio_mksock_line(const char *line)
-{
- return cpio_mkgeneric_line(line, GT_SOCK);
-}
-
-static int cpio_mknod(const char *name, unsigned int mode,
- uid_t uid, gid_t gid, char dev_type,
- unsigned int maj, unsigned int min)
-{
- int len;
- unsigned int namesize;
-
- if (dev_type == 'b')
- mode |= S_IFBLK;
- else
- mode |= S_IFCHR;
-
- if (name[0] == '/')
- name++;
- namesize = strlen(name) + 1;
-
- len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
- "%08X%08X%08X%08X%08X%08X%08X",
- do_csum ? "070702" : "070701", /* magic */
- ino++, /* ino */
- mode, /* mode */
- (long) uid, /* uid */
- (long) gid, /* gid */
- 1, /* nlink */
- (long) default_mtime, /* mtime */
- 0, /* filesize */
- 3, /* major */
- 1, /* minor */
- maj, /* rmajor */
- min, /* rminor */
- namesize, /* namesize */
- 0); /* chksum */
- offset += len;
-
- if (len != CPIO_HDR_LEN ||
- push_rest(name, namesize) < 0)
- return -1;
-
- return 0;
-}
-
-static int cpio_mknod_line(const char *line)
-{
- char name[PATH_MAX + 1];
- unsigned int mode;
- int uid;
- int gid;
- char dev_type;
- unsigned int maj;
- unsigned int min;
- int rc = -1;
-
- if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
- name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
- fprintf(stderr, "Unrecognized nod format '%s'", line);
- goto fail;
- }
- rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
- fail:
- return rc;
-}
-
-static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum)
-{
- while (size) {
- unsigned char filebuf[65536];
- ssize_t this_read;
- size_t i, this_size = MIN(size, sizeof(filebuf));
-
- this_read = read(fd, filebuf, this_size);
- if (this_read <= 0 || this_read > this_size)
- return -1;
-
- for (i = 0; i < this_read; i++)
- *csum += filebuf[i];
-
- size -= this_read;
- }
- /* seek back to the start for data segment I/O */
- if (lseek(fd, 0, SEEK_SET) < 0)
- return -1;
-
- return 0;
-}
-
-static int cpio_mkfile(const char *name, const char *location,
- unsigned int mode, uid_t uid, gid_t gid,
- unsigned int nlinks)
-{
- struct stat buf;
- unsigned long size;
- int file, retval, len;
- int rc = -1;
- time_t mtime;
- int namesize, namepadlen;
- unsigned int i;
- uint32_t csum = 0;
- ssize_t this_read;
-
- mode |= S_IFREG;
-
- file = open (location, O_RDONLY);
- if (file < 0) {
- fprintf (stderr, "File %s could not be opened for reading\n", location);
- goto error;
- }
-
- retval = fstat(file, &buf);
- if (retval) {
- fprintf(stderr, "File %s could not be stat()'ed\n", location);
- goto error;
- }
-
- if (do_file_mtime) {
- mtime = default_mtime;
- } else {
- mtime = buf.st_mtime;
- if (mtime > 0xffffffff) {
- fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
- location);
- mtime = 0xffffffff;
- }
-
- if (mtime < 0) {
- fprintf(stderr, "%s: Timestamp negative, clipping.\n",
- location);
- mtime = 0;
- }
- }
-
- if (buf.st_size > 0xffffffff) {
- fprintf(stderr, "%s: Size exceeds maximum cpio file size\n",
- location);
- goto error;
- }
-
- if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) {
- fprintf(stderr, "Failed to checksum file %s\n", location);
- goto error;
- }
-
- size = 0;
- namepadlen = 0;
- for (i = 1; i <= nlinks; i++) {
- if (name[0] == '/')
- name++;
- namesize = strlen(name) + 1;
-
- /* data goes on last link, after any alignment padding */
- if (i == nlinks)
- size = buf.st_size;
-
- if (dalign && size > dalign) {
- namepadlen = padlen(offset + CPIO_HDR_LEN + namesize,
- dalign);
- if (namesize + namepadlen > PATH_MAX) {
- fprintf(stderr,
- "%s: best-effort alignment %u missed\n",
- name, dalign);
- namepadlen = 0;
- }
- }
-
- len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"
- "%08lX%08X%08X%08X%08X%08X%08X",
- do_csum ? "070702" : "070701", /* magic */
- ino, /* ino */
- mode, /* mode */
- (long) uid, /* uid */
- (long) gid, /* gid */
- nlinks, /* nlink */
- (long) mtime, /* mtime */
- size, /* filesize */
- 3, /* major */
- 1, /* minor */
- 0, /* rmajor */
- 0, /* rminor */
- namesize + namepadlen, /* namesize */
- size ? csum : 0); /* chksum */
- offset += len;
-
- if (len != CPIO_HDR_LEN ||
- push_buf(name, namesize) < 0 ||
- push_pad(namepadlen ? namepadlen : padlen(offset, 4)) < 0)
- goto error;
-
- if (size) {
- this_read = copy_file_range(file, NULL, outfd, NULL, size, 0);
- if (this_read > 0) {
- if (this_read > size)
- goto error;
- offset += this_read;
- size -= this_read;
- }
- /* short or failed copy falls back to read/write... */
- }
-
- while (size) {
- unsigned char filebuf[65536];
- size_t this_size = MIN(size, sizeof(filebuf));
-
- this_read = read(file, filebuf, this_size);
- if (this_read <= 0 || this_read > this_size) {
- fprintf(stderr, "Can not read %s file\n", location);
- goto error;
- }
-
- if (write(outfd, filebuf, this_read) != this_read) {
- fprintf(stderr, "writing filebuf failed\n");
- goto error;
- }
- offset += this_read;
- size -= this_read;
- }
- if (push_pad(padlen(offset, 4)) < 0)
- goto error;
-
- name += namesize;
- }
- ino++;
- rc = 0;
-
-error:
- if (file >= 0)
- close(file);
- return rc;
-}
-
-static char *cpio_replace_env(char *new_location)
-{
- char expanded[PATH_MAX + 1];
- char *start, *end, *var;
-
- while ((start = strstr(new_location, "${")) &&
- (end = strchr(start + 2, '}'))) {
- *start = *end = 0;
- var = getenv(start + 2);
- snprintf(expanded, sizeof expanded, "%s%s%s",
- new_location, var ? var : "", end + 1);
- strcpy(new_location, expanded);
- }
-
- return new_location;
-}
-
-static int cpio_mkfile_line(const char *line)
-{
- char name[PATH_MAX + 1];
- char *dname = NULL; /* malloc'ed buffer for hard links */
- char location[PATH_MAX + 1];
- unsigned int mode;
- int uid;
- int gid;
- int nlinks = 1;
- int end = 0, dname_len = 0;
- int rc = -1;
-
- if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
- "s %o %d %d %n",
- name, location, &mode, &uid, &gid, &end)) {
- fprintf(stderr, "Unrecognized file format '%s'", line);
- goto fail;
- }
- if (end && isgraph(line[end])) {
- int len;
- int nend;
-
- dname = malloc(strlen(line));
- if (!dname) {
- fprintf (stderr, "out of memory (%d)\n", dname_len);
- goto fail;
- }
-
- dname_len = strlen(name) + 1;
- memcpy(dname, name, dname_len);
-
- do {
- nend = 0;
- if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
- name, &nend) < 1)
- break;
- len = strlen(name) + 1;
- memcpy(dname + dname_len, name, len);
- dname_len += len;
- nlinks++;
- end += nend;
- } while (isgraph(line[end]));
- } else {
- dname = name;
- }
- rc = cpio_mkfile(dname, cpio_replace_env(location),
- mode, uid, gid, nlinks);
- fail:
- if (dname_len) free(dname);
- return rc;
-}
-
-static void usage(const char *prog)
-{
- fprintf(stderr, "Usage:\n"
- "\t%s [-t <timestamp>] [-c] [-o <output_file>] [-a <data_align>] <cpio_list>\n"
- "\n"
- "<cpio_list> is a file containing newline separated entries that\n"
- "describe the files to be included in the initramfs archive:\n"
- "\n"
- "# a comment\n"
- "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
- "dir <name> <mode> <uid> <gid>\n"
- "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
- "slink <name> <target> <mode> <uid> <gid>\n"
- "pipe <name> <mode> <uid> <gid>\n"
- "sock <name> <mode> <uid> <gid>\n"
- "\n"
- "<name> name of the file/dir/nod/etc in the archive\n"
- "<location> location of the file in the current filesystem\n"
- " expands shell variables quoted with ${}\n"
- "<target> link target\n"
- "<mode> mode/permissions of the file\n"
- "<uid> user id (0=root)\n"
- "<gid> group id (0=root)\n"
- "<dev_type> device type (b=block, c=character)\n"
- "<maj> major number of nod\n"
- "<min> minor number of nod\n"
- "<hard links> space separated list of other links to file\n"
- "\n"
- "example:\n"
- "# A simple initramfs\n"
- "dir /dev 0755 0 0\n"
- "nod /dev/console 0600 0 0 c 5 1\n"
- "dir /root 0700 0 0\n"
- "dir /sbin 0755 0 0\n"
- "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
- "\n"
- "<timestamp> is time in seconds since Epoch that will be used\n"
- "as mtime for symlinks, directories, regular and special files.\n"
- "The default is to use the current time for all files, but\n"
- "preserve modification time for regular files.\n"
- "-c: calculate and store 32-bit checksums for file data.\n"
- "<output_file>: write cpio to this file instead of stdout\n"
- "<data_align>: attempt to align file data by zero-padding the\n"
- "filename field up to data_align. Must be a multiple of 4.\n"
- "Alignment is best-effort; PATH_MAX limits filename padding.\n",
- prog);
-}
-
-static const struct file_handler file_handler_table[] = {
- {
- .type = "file",
- .handler = cpio_mkfile_line,
- }, {
- .type = "nod",
- .handler = cpio_mknod_line,
- }, {
- .type = "dir",
- .handler = cpio_mkdir_line,
- }, {
- .type = "slink",
- .handler = cpio_mkslink_line,
- }, {
- .type = "pipe",
- .handler = cpio_mkpipe_line,
- }, {
- .type = "sock",
- .handler = cpio_mksock_line,
- }, {
- .type = NULL,
- .handler = NULL,
- }
-};
-
-#define LINE_SIZE (2 * PATH_MAX + 50)
-
-int main (int argc, char *argv[])
-{
- FILE *cpio_list;
- char line[LINE_SIZE];
- char *args, *type;
- int ec = 0;
- int line_nr = 0;
- const char *filename;
-
- default_mtime = time(NULL);
- while (1) {
- int opt = getopt(argc, argv, "t:cho:a:");
- char *invalid;
-
- if (opt == -1)
- break;
- switch (opt) {
- case 't':
- default_mtime = strtol(optarg, &invalid, 10);
- if (!*optarg || *invalid) {
- fprintf(stderr, "Invalid timestamp: %s\n",
- optarg);
- usage(argv[0]);
- exit(1);
- }
- do_file_mtime = true;
- break;
- case 'c':
- do_csum = true;
- break;
- case 'o':
- outfd = open(optarg,
- O_WRONLY | O_CREAT | O_LARGEFILE | O_TRUNC,
- 0600);
- if (outfd < 0) {
- fprintf(stderr, "failed to open %s\n", optarg);
- usage(argv[0]);
- exit(1);
- }
- break;
- case 'a':
- dalign = strtoul(optarg, &invalid, 10);
- if (!*optarg || *invalid || (dalign & 3)) {
- fprintf(stderr, "Invalid data_align: %s\n",
- optarg);
- usage(argv[0]);
- exit(1);
- }
- break;
- case 'h':
- case '?':
- usage(argv[0]);
- exit(opt == 'h' ? 0 : 1);
- }
- }
-
- /*
- * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
- * representation that exceeds 8 chars and breaks the cpio header
- * specification. Negative timestamps similarly exceed 8 chars.
- */
- if (default_mtime > 0xffffffff || default_mtime < 0) {
- fprintf(stderr, "ERROR: Timestamp out of range for cpio format\n");
- exit(1);
- }
-
- if (argc - optind != 1) {
- usage(argv[0]);
- exit(1);
- }
- filename = argv[optind];
- if (!strcmp(filename, "-"))
- cpio_list = stdin;
- else if (!(cpio_list = fopen(filename, "r"))) {
- fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
- filename, strerror(errno));
- usage(argv[0]);
- exit(1);
- }
-
- while (fgets(line, LINE_SIZE, cpio_list)) {
- int type_idx;
- size_t slen = strlen(line);
-
- line_nr++;
-
- if ('#' == *line) {
- /* comment - skip to next line */
- continue;
- }
-
- if (! (type = strtok(line, " \t"))) {
- fprintf(stderr,
- "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
- line_nr, line);
- ec = -1;
- break;
- }
-
- if ('\n' == *type) {
- /* a blank line */
- continue;
- }
-
- if (slen == strlen(type)) {
- /* must be an empty line */
- continue;
- }
-
- if (! (args = strtok(NULL, "\n"))) {
- fprintf(stderr,
- "ERROR: incorrect format, newline required line %d: '%s'\n",
- line_nr, line);
- ec = -1;
- }
-
- for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
- int rc;
- if (! strcmp(line, file_handler_table[type_idx].type)) {
- if ((rc = file_handler_table[type_idx].handler(args))) {
- ec = rc;
- fprintf(stderr, " line %d\n", line_nr);
- }
- break;
- }
- }
-
- if (NULL == file_handler_table[type_idx].type) {
- fprintf(stderr, "unknown file type line %d: '%s'\n",
- line_nr, line);
- }
- }
- if (ec == 0)
- ec = cpio_trailer();
-
- exit(ec);
-}