summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-07-10 11:31:46 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-07-10 11:31:46 +0800
commite90b72ec3082588b296956730bd2930cf8d5d671 (patch)
tree36ff6916af9b4176ea5a9662219d11e721f0b691 /include
parent1767d390fbf3d0c65389de46caf0b6d43ef3b6ad (diff)
downloadlume-e90b72ec3082588b296956730bd2930cf8d5d671.tar.gz
lume-e90b72ec3082588b296956730bd2930cf8d5d671.tar.xz
refactor: promote new architecture to repository root
Archive the deprecated implementation under lume-legacy, make the new kernel the default root-level build, and update repository entry points without changing kernel behavior.
Diffstat (limited to 'include')
-rw-r--r--include/common/fcntl.h14
-rw-r--r--include/common/types.h32
-rw-r--r--include/drivers/plic.h22
-rw-r--r--include/drivers/uart.h38
-rw-r--r--include/drivers/uart_color.h12
-rw-r--r--include/drivers/virtio.h108
-rw-r--r--include/fs/fat32.h122
-rw-r--r--include/fs/file.h78
-rw-r--r--include/fs/fs.h105
-rw-r--r--include/fs/stat.h27
-rw-r--r--include/kernel/buf.h35
-rw-r--r--include/kernel/config.h12
-rw-r--r--include/kernel/cpu.h24
-rw-r--r--include/kernel/elf.h51
-rw-r--r--include/kernel/exec.h11
-rw-r--r--include/kernel/fdt.h45
-rw-r--r--include/kernel/mm.h32
-rw-r--r--include/kernel/pmm.h60
-rw-r--r--include/kernel/proc.h34
-rw-r--r--include/kernel/proc_def.h75
-rw-r--r--include/kernel/riscv.h404
-rw-r--r--include/kernel/runqueue.h21
-rw-r--r--include/kernel/sbi.h29
-rw-r--r--include/kernel/slab.h15
-rw-r--r--include/kernel/spinlock.h53
-rw-r--r--include/kernel/sync.h43
-rw-r--r--include/kernel/syscall.h85
-rw-r--r--include/kernel/timer.h17
-rw-r--r--include/kernel/trap.h55
-rw-r--r--include/lib/errno.h168
-rw-r--r--include/lib/string.h19
-rw-r--r--include/libc-shim/bits/alltypes.h15
-rw-r--r--include/libc-shim/endian.h9
-rw-r--r--include/libc-shim/features.h12
-rw-r--r--include/libc-shim/limits.h17
-rw-r--r--include/libc-shim/stdint.h21
-rw-r--r--include/libc-shim/string.h110
-rw-r--r--include/libc-shim/strings.h40
-rw-r--r--include/lume/addr.h91
-rw-r--r--include/lume/addr_types.h29
-rw-r--r--include/lume/args.h28
-rw-r--r--include/lume/atomic.h76
-rw-r--r--include/lume/bitops.h120
-rw-r--r--include/lume/chardev.h45
-rw-r--r--include/lume/clone.h34
-rw-r--r--include/lume/compiler.h54
-rw-r--r--include/lume/config.h100
-rw-r--r--include/lume/console.h77
-rw-r--r--include/lume/container_of.h17
-rw-r--r--include/lume/dcache.h143
-rw-r--r--include/lume/device.h75
-rw-r--r--include/lume/device_node.h64
-rw-r--r--include/lume/driver.h24
-rw-r--r--include/lume/elf.h150
-rw-r--r--include/lume/errno.h186
-rw-r--r--include/lume/fdt.h85
-rw-r--r--include/lume/fdt_prop.h24
-rw-r--r--include/lume/fdtable.h49
-rw-r--r--include/lume/features.h40
-rw-r--r--include/lume/file.h132
-rw-r--r--include/lume/fs.h310
-rw-r--r--include/lume/fs_struct.h39
-rw-r--r--include/lume/fs_types.h197
-rw-r--r--include/lume/init.h33
-rw-r--r--include/lume/irq.h84
-rw-r--r--include/lume/kbuild.h40
-rw-r--r--include/lume/kernel_map.h55
-rw-r--r--include/lume/klog.h200
-rw-r--r--include/lume/kobject.h57
-rw-r--r--include/lume/kprintf.h39
-rw-r--r--include/lume/kref.h46
-rw-r--r--include/lume/libfdt.h8
-rw-r--r--include/lume/libfdt_env.h78
-rw-r--r--include/lume/limits.h48
-rw-r--r--include/lume/list.h346
-rw-r--r--include/lume/lumeos.lds.h193
-rw-r--r--include/lume/memblock.h53
-rw-r--r--include/lume/mm_layout.h73
-rw-r--r--include/lume/mman.h21
-rw-r--r--include/lume/mount.h52
-rw-r--r--include/lume/mutex.h63
-rw-r--r--include/lume/namei.h32
-rw-r--r--include/lume/new.h18
-rw-r--r--include/lume/page.h151
-rw-r--r--include/lume/page_cache.h82
-rw-r--r--include/lume/pgrp.h125
-rw-r--r--include/lume/pgtable_types.h69
-rw-r--r--include/lume/pid.h29
-rw-r--r--include/lume/pipe.h52
-rw-r--r--include/lume/platform_device.h64
-rw-r--r--include/lume/pmm.h108
-rw-r--r--include/lume/poll.h68
-rw-r--r--include/lume/ptrace.h76
-rw-r--r--include/lume/random.h38
-rw-r--r--include/lume/rbtree.h207
-rw-r--r--include/lume/rbtree_augmented.h252
-rw-r--r--include/lume/rbtree_types.h28
-rw-r--r--include/lume/sched.h102
-rw-r--r--include/lume/sched_class.h35
-rw-r--r--include/lume/selftest.h65
-rw-r--r--include/lume/setup.h42
-rw-r--r--include/lume/signal.h60
-rw-r--r--include/lume/signal_nr.h54
-rw-r--r--include/lume/signal_types.h263
-rw-r--r--include/lume/slab.h28
-rw-r--r--include/lume/smp.h74
-rw-r--r--include/lume/stdarg.h11
-rw-r--r--include/lume/stringify.h14
-rw-r--r--include/lume/syscall.h38
-rw-r--r--include/lume/syscall_nr.h82
-rw-r--r--include/lume/task.h144
-rw-r--r--include/lume/timer.h43
-rw-r--r--include/lume/tlb.h48
-rw-r--r--include/lume/trap.h109
-rw-r--r--include/lume/tty.h165
-rw-r--r--include/lume/tty_ioctl.h202
-rw-r--r--include/lume/types.h78
-rw-r--r--include/lume/uaccess.h27
-rw-r--r--include/lume/uuid.h207
-rw-r--r--include/lume/vmm.h115
-rw-r--r--include/lume/wait.h31
-rw-r--r--include/lume/waitqueue.h79
-rw-r--r--include/ulib/assert.h25
-rw-r--r--include/ulib/cstdlib57
-rw-r--r--include/ulib/cstring37
-rw-r--r--include/ulib/ctypes.h31
-rw-r--r--include/ulib/cxx.cc37
-rw-r--r--include/ulib/errno.h170
-rw-r--r--include/ulib/iostream115
-rw-r--r--include/ulib/limits.h26
-rw-r--r--include/ulib/stb_sprintf.h1916
-rw-r--r--include/ulib/stdio.h101
-rw-r--r--include/ulib/stdlib.h100
-rw-r--r--include/ulib/string.h74
-rw-r--r--include/ulib/types.h25
-rw-r--r--include/user/user.h75
136 files changed, 7417 insertions, 4635 deletions
diff --git a/include/common/fcntl.h b/include/common/fcntl.h
deleted file mode 100644
index 6059538..0000000
--- a/include/common/fcntl.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-
-#define O_RDONLY 0x000
-#define O_WRONLY 0x001
-#define O_RDWR 0x002
-#define O_CREATE 0x200
-#define O_TRUNC 0x400
-
-#define NOFILE 16 \ No newline at end of file
diff --git a/include/common/types.h b/include/common/types.h
deleted file mode 100644
index aa2cb08..0000000
--- a/include/common/types.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-typedef unsigned long long uint64_t;
-
-typedef signed char int8_t;
-typedef signed short int16_t;
-typedef signed int int32_t;
-typedef signed long long int64_t;
-
-typedef uint64_t size_t;
-typedef uint64_t uintptr_t;
-
-typedef uint64_t uint64;
-typedef uint32_t uint32;
-typedef uint16_t uint16;
-typedef uint8_t uint8;
-typedef unsigned int uint;
-typedef unsigned short ushort;
-typedef unsigned char uchar;
-
-typedef int64_t int64;
-typedef int32_t int32;
-typedef int16_t int16;
-typedef int8_t int8; \ No newline at end of file
diff --git a/include/drivers/plic.h b/include/drivers/plic.h
deleted file mode 100644
index cdcd0ce..0000000
--- a/include/drivers/plic.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-namespace PLIC
-{
- // Global initialization (executed only once by hart 0)
- void init();
-
- // Per-core initialization (executed when each CPU starts)
- void inithart();
-
- // Claim interrupt: Get the currently occurring interrupt number (IRQ)
- int claim();
-
- // End of interrupt: notify the PLIC that this IRQ has been handled
- void complete(int irq);
-} // namespace PILC
diff --git a/include/drivers/uart.h b/include/drivers/uart.h
deleted file mode 100644
index a56624c..0000000
--- a/include/drivers/uart.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard Qin
- */
-
-#pragma once
-#include "common/types.h"
-#include "drivers/uart_color.h"
-#include "kernel/fdt.h"
-
-// UART register offsets (NS16550A)
-#define RHR 0 // Receive Holding Register (read mode)
-#define THR 0 // Transmit Holding Register (write mode)
-#define IER 1 // Interrupt Enable Register
-#define FCR 2 // FIFO Control Register
-#define ISR 2 // Interrupt Status Register
-#define LCR 3 // Line Control Register
-#define LSR 5 // Line Status Register
-
-#define LSR_RX_READY 0x01 // input is waiting to be read from RHR
-#define LSR_TX_IDLE 0x20 // THR can accept another character to send
-
-#define UART_RX_BUF_SIZE 128
-
-// UART base address from FDT (use inline function to avoid macro issues)
-static inline uint64 uart_base() { return g_devices.uart.base_addr; }
-
-namespace Drivers
-{
- void uart_init();
- void uart_intr();
- int console_read(uint64 target, int len);
-
- void uart_putc(char c);
- void uart_puts(const char *s);
- void uart_put_int(int value);
- void print_hex(uint64 value);
-} \ No newline at end of file
diff --git a/include/drivers/uart_color.h b/include/drivers/uart_color.h
deleted file mode 100644
index 7ecc28b..0000000
--- a/include/drivers/uart_color.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-// ANSI Escape Codes for Modern Terminal Colors
-#define ANSI_RESET "\033[0m"
-#define ANSI_RED "\033[31m"
-#define ANSI_GREEN "\033[32m"
-#define ANSI_YELLOW "\033[33m"
-#define ANSI_BLUE "\033[34m"
-#define ANSI_CLEAR "\033[2J\033[1;1H" \ No newline at end of file
diff --git a/include/drivers/virtio.h b/include/drivers/virtio.h
deleted file mode 100644
index 9fda8cf..0000000
--- a/include/drivers/virtio.h
+++ /dev/null
@@ -1,108 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-// VirtIO MMIO Base Address
-#define VIRTIO0 0x10001000
-
-// MMIO Registers (Modern / Version 2)
-#define VIRTIO_MMIO_MAGIC_VALUE 0x000
-#define VIRTIO_MMIO_VERSION 0x004
-#define VIRTIO_MMIO_DEVICE_ID 0x008
-#define VIRTIO_MMIO_VENDOR_ID 0x00c
-#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
-#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014
-#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
-#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024
-#define VIRTIO_MMIO_QUEUE_SEL 0x030
-#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034
-#define VIRTIO_MMIO_QUEUE_NUM 0x038
-#define VIRTIO_MMIO_QUEUE_READY 0x044
-#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050
-#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060
-#define VIRTIO_MMIO_INTERRUPT_ACK 0x064
-#define VIRTIO_MMIO_STATUS 0x070
-
-// 64-bit Queue Address Registers (Modern)
-#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080
-#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084
-#define VIRTIO_MMIO_QUEUE_DRIVER_LOW 0x090
-#define VIRTIO_MMIO_QUEUE_DRIVER_HIGH 0x094
-#define VIRTIO_MMIO_QUEUE_DEVICE_LOW 0x0a0
-#define VIRTIO_MMIO_QUEUE_DEVICE_HIGH 0x0a4
-
-// Status Bits
-#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
-#define VIRTIO_CONFIG_S_DRIVER 2
-#define VIRTIO_CONFIG_S_DRIVER_OK 4
-#define VIRTIO_CONFIG_S_FEATURES_OK 8
-
-// Feature Bits
-#define VIRTIO_BLK_F_RO 5
-#define VIRTIO_BLK_F_SCSI 7
-#define VIRTIO_BLK_F_CONFIG_WCE 11
-#define VIRTIO_BLK_F_MQ 12
-#define VIRTIO_F_ANY_LAYOUT 27
-#define VIRTIO_RING_F_INDIRECT_DESC 28
-#define VIRTIO_RING_F_EVENT_IDX 29
-#define VIRTIO_F_VERSION_1 32
-
-// Descriptor Flags
-#define VRING_DESC_F_NEXT 1
-#define VRING_DESC_F_WRITE 2
-
-#define NUM_DESCS 8
-
-#define VIRTIO_BLK_T_IN 0
-#define VIRTIO_BLK_T_OUT 1
-
-// Device Configuration Layout (Offset 0x100)
-#define VIRTIO_MMIO_CONFIG 0x100
-
-namespace VirtIO
-{
- struct VRingDesc
- {
- uint64 addr;
- uint32 len;
- uint16 flags;
- uint16 next;
- };
-
- struct VRingAvail
- {
- uint16 flags;
- uint16 idx;
- uint16 ring[NUM_DESCS];
- };
-
- struct VRingUsedElem
- {
- uint32 id;
- uint32 len;
- };
-
- struct VRingUsed
- {
- uint16 flags;
- uint16 idx;
- struct VRingUsedElem ring[NUM_DESCS];
- };
-
- struct __attribute__((packed)) BlockRequest
- {
- uint32 type; // 0=Read, 1=Write
- uint32 reserved;
- uint64 sector;
- };
-
- void init();
- void intr();
- void rw(int write, uint64 blockno, char *buf);
- void test_rw();
- void test_bio();
-} \ No newline at end of file
diff --git a/include/fs/fat32.h b/include/fs/fat32.h
deleted file mode 100644
index 63d62ae..0000000
--- a/include/fs/fat32.h
+++ /dev/null
@@ -1,122 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "fs/fs.h"
-#include "kernel/buf.h"
-
-// Disk Layout Structures (On-Disk)
-
-// FAT32 Boot Sector (BPB)
-struct FAT32_BPB
-{
- uint8 jmpBoot[3];
- char oemName[8];
- uint16 bytesPerSector;
- uint8 secPerClus;
- uint16 reservedSecCnt;
- uint8 numFATs;
- uint16 rootEntCnt;
- uint16 totSec16;
- uint8 media;
- uint16 FATSz16;
- uint16 secPerTrk;
- uint16 numHeads;
- uint32 hiddSec;
- uint32 totSec32;
- uint32 FATSz32;
- uint16 extFlags;
- uint16 FSVer;
- uint32 rootClus;
- uint16 FSInfo;
- uint16 bkBootSec;
- uint8 reserved[12];
- uint8 drvNum;
- uint8 reserved1;
- uint8 bootSig;
- uint32 volID;
- char volLab[11];
- char filSysType[8];
-} __attribute__((packed));
-
-// FAT32 Directory Entry (32 bytes)
-struct FAT32_DirEnt
-{
- char name[11]; // 8.3 filename
- uint8 attr; // File attributes
- uint8 ntRes;
- uint8 crtTimeTenth;
- uint16 crtTime;
- uint16 crtDate;
- uint16 lstAccDate;
- uint16 fstClusHI; // High 16 bits of first cluster
- uint16 wrtTime;
- uint16 wrtDate;
- uint16 fstClusLO; // Low 16 bits of first cluster
- uint32 fileSize;
-} __attribute__((packed));
-
-#define ATTR_READ_ONLY 0x01
-#define ATTR_HIDDEN 0x02
-#define ATTR_SYSTEM 0x04
-#define ATTR_VOLUME_ID 0x08
-#define ATTR_DIRECTORY 0x10
-#define ATTR_ARCHIVE 0x20
-#define ATTR_LONG_NAME 0x0F
-
-// In-Memory Classes
-
-class FAT32FileSystem;
-
-class FAT32Inode : public Inode
-{
-public:
- FAT32FileSystem *fs;
-
- // FAT32 Private Data
- uint32 first_clus; // The starting cluster number of the file
- uint32 parent_clus; // Starting cluster number of the parent directory
- uint32 dir_offset; // The byte offset of this directory entry in the parent directory
- uint32 ent_sec; // Sector number of the directory entry
- uint32 ent_off; // Offset of the directory entry within the sector
-
- FAT32Inode() : fs(nullptr), first_clus(0) {}
- // Implement VFS Interface
- void update() override;
- int read(char *dst, uint32 off, uint32 n, int isUser) override;
- int write(const char *src, uint32 off, uint32 n, int isUser) override;
- Inode *lookup(const char *name) override;
- Inode *create(const char *name, short type, short major, short minor) override;
- void truncate() override;
- int unlink(const char *name) override;
-};
-
-class FAT32FileSystem : public FileSystem
-{
-public:
- uint32 fat_start_sector;
- uint32 data_start_sector;
- uint32 root_cluster;
- uint32 sectors_per_cluster;
- uint32 bytes_per_cluster;
- uint32 fat_sz; // Size of the FAT table (number of sectors)
-
- void init() override;
- Inode* root() override;
-
- // Helper functions
- uint32 next_cluster(uint32 cluster); // Check the FAT table for the next cluster
- uint32 alloc_cluster(); // Assign a new cluster
- uint32 cluster_to_sector(uint32 cluster);
-
- // directory entry parsing
- void read_dir_ent(struct FAT32_DirEnt *de, char *filename);
- // Release the entire cluster chain starting from the cluster
- void free_cluster_chain(uint32 cluster);
-};
-
-extern FAT32FileSystem fat32_fs;
-
-void fat32_test(); \ No newline at end of file
diff --git a/include/fs/file.h b/include/fs/file.h
deleted file mode 100644
index 9aff1af..0000000
--- a/include/fs/file.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-#include "fs/fs.h"
-
-enum FileType
-{
- FD_NONE,
- FD_INODE,
- FD_DEVICE,
- FD_PIPE
-};
-
-class Pipe;
-
-struct file
-{
- enum FileType type;
- int ref;
- char readable;
- char writable;
-
- Pipe *pipe; // pipeline pointer
- Inode *ip; // VFS Inode
- uint32 off; // Current Offset
-};
-
-class Pipe
-{
-public:
- static constexpr uint32_t SIZE = 512;
-
- Pipe() : nread(0), nwrite(0), read_open(true), write_open(true)
- {
- lock.init("pipe");
- }
-
- // forbid copy
- Pipe(const Pipe &) = delete;
- Pipe &operator=(const Pipe &) = delete;
-
- ~Pipe()
- {
- //
- }
-
- // creates a pair of connected file objects
- static int create_pair(struct file **f0, struct file **f1);
-
- int read(uint64_t addr, int n);
- int write(uint64_t addr, int n);
- void close(bool is_writer);
-
-private:
- Spinlock lock;
- char data[SIZE];
- uint32_t nread; // read
- uint32_t nwrite; // write
- bool read_open;
- bool write_open;
-};
-
-namespace FileTable
-{
- void init();
- struct file *alloc(); // Allocate a free file handle
- struct file *dup(struct file *f); // Duplicate handle (sys_dup)
- void close(struct file *f); // Close handle
-
- int stat(struct file *f, uint64 addr);
- int read(struct file *f, uint64 addr, int n);
- int write(struct file *f, uint64 addr, int n);
- int lseek(struct file *f, int offset, int whence);
-} // namespace FileTable
diff --git a/include/fs/fs.h b/include/fs/fs.h
deleted file mode 100644
index b511a84..0000000
--- a/include/fs/fs.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-#include "kernel/spinlock.h"
-#include "fs/stat.h"
-
-class FileSystem;
-
-// VFS Inode (Abstract Base Class)
-
-class Inode
-{
-private:
- ;
-public:
- // VFS Common Fields
- uint32 dev; // Device number (VirtIO Block ID)
- uint32 inum; // Inode number
- int ref_cnt; // Memory reference count (number of struct file or processes referencing it)
- int valid; // Whether the data is valid (whether metadata has been read from disk)
-
- Mutex lock; // Sleep lock: protects internal Inode data and atomicity of read/write operations
-
- // Metadata Cache (for speeding up getattr)
- short type; // T_DIR, T_FILE, T_DEVICE
- short major; // Major device number (only for T_DEVICE)
- short minor; // Minor device number (only for T_DEVICE)
- short nlink; // Number of hard links
- uint32 size; // File size
-
- Inode() : ref_cnt(0), valid(0)
- {
- lock.init("inode");
- }
- virtual ~Inode() {}
-
- /* Core Operation Interface (Subclass Must Implement) */
- // Update metadata
- // Sync size, type, links, etc. from memory back to disk
- virtual void update() = 0;
- // Read content
- // Read n bytes from the file at offset into dst
- // Returns the actual number of bytes read, returns -1 on error
- virtual int read(char *dst, uint32 off, uint32 n, int isUser) = 0;
- // 3. Write content
- // Write n bytes from src to the file at the given offset
- // Need to handle file growth
- virtual int write(const char *src, uint32 off, uint32 n, int isUser) = 0;
- // Directory Lookup (only valid when type == T_DIR)
- // Search for a file named 'name' in the current directory and return its Inode pointer (locked)
- // Return nullptr if not found
- virtual Inode *lookup(const char *name) = 0;
- // Create file/directory (only effective when type == T_DIR)
- // Create a new entry in the current directory
- virtual Inode *create(const char *name, short type, short major, short minor) = 0;
- // Get file status (populate the stat structure)
- // Default implementation, subclasses can override to provide more detailed information
- virtual void getattr(struct kstat *st);
- // Truncate file (set size to 0, release all data blocks)
- virtual void truncate() = 0;
- // Delete the file named 'name' from the directory (Unlink)
- virtual int unlink(const char *name) = 0;
-};
-
-// File System Abstraction (SuperBlock)
-class FileSystem
-{
-private:
- ;
-public:
- virtual void init() = 0;
- virtual Inode* root() = 0;
- virtual ~FileSystem() {};
-};
-
-// Inode Cache Interface (Global Function)
-namespace VFS
-{
- void init();
-
- // Decrease the reference count, and if it reaches 0, free the memory
- void iput(Inode *ip);
- // Increase reference count
- Inode *idup(Inode *ip);
- // lock and load data
- void ilock(Inode *ip);
- // unlock
- void iunlock(Inode *ip);
- void iunlockput(Inode *ip);
- // Mount the root filesystem
- void mount_root(Inode *root);
- // Get system root node
- Inode *get_root();
- // Path Analysis
- Inode *namei(const char *path);
- // Find the parent directory inode of the path, and copy the last part of the path (the file name) into the name buffer
- Inode *nameiparent(const char *path, char *name);
- // Register a file system
- void register_fs(FileSystem *fs);
-
-} // namespace VFS
diff --git a/include/fs/stat.h b/include/fs/stat.h
deleted file mode 100644
index e60ae42..0000000
--- a/include/fs/stat.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-#define T_DIR 1 // Directory
-#define T_FILE 2 // File
-#define T_DEVICE 3 // Device
-
-struct kstat
-{
- int dev; // File System's disk device
- uint32 ino; // Inode number
- short type; //Type of file
- short nlink; // Number of links to file
- uint64 size; // Size of file in bytes
-
- uint32 mode; // Protection: rwx
- uint32 uid; // User ID
- uint32 gid; // Group ID
- uint32 atime; // Access time
- uint32 mtime; // Modified time
- uint32 ctime; // Change time
-};
diff --git a/include/kernel/buf.h b/include/kernel/buf.h
deleted file mode 100644
index d0d849c..0000000
--- a/include/kernel/buf.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-#include "kernel/spinlock.h"
-
-#define BSIZE 1024
-#define NBUF 30
-
-struct Buf
-{
- int valid; // Whether the data has been read from the disk (1=valid, 0=invalid)
- int disk; // Whether it has been held by the driver (dirty bit/write-back flag is usually extended here)
- uint32 dev; // Device Number
- uint32 blockno; // Block number
- uint32 refcnt; // Reference Counting
- struct Buf *prev; // LRU linked list
- struct Buf *next;
- uchar data[BSIZE];
-
- Mutex lock; // Sleep Lock
-};
-
-namespace BufferCache
-{
- void init();
- struct Buf *bread(uint32 dev, uint32 blockno);
- void bwrite(struct Buf *b);
- void brelse(struct Buf *b);
- void bpin(struct Buf *b);
- void bunpin(struct Buf *b);
-} // namespace BufferCache
diff --git a/include/kernel/config.h b/include/kernel/config.h
deleted file mode 100644
index 49df2ff..0000000
--- a/include/kernel/config.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard Qin
- */
-
-#pragma once
-#include "common/types.h"
-#include "kernel/fdt.h"
-
-// Legacy compat — these are still referenced in some places
-extern uint64 g_uart_base;
-extern uint64 g_dtb_addr; \ No newline at end of file
diff --git a/include/kernel/cpu.h b/include/kernel/cpu.h
deleted file mode 100644
index 62bdc17..0000000
--- a/include/kernel/cpu.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-#include "kernel/proc_def.h"
-
-struct RunQueue;
-
-struct cpu
-{
- struct Proc *proc;
- struct Context context;
- int noff;
- int intena;
-
- struct RunQueue *runqueue;
-};
-
-
-extern struct cpu cpus[];
-struct cpu *mycpu(); \ No newline at end of file
diff --git a/include/kernel/elf.h b/include/kernel/elf.h
deleted file mode 100644
index 30193d2..0000000
--- a/include/kernel/elf.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-#define ELF_MAGIC 0x464C457F // "\x7FELF" in little endian
-
-// ELF File Header
-struct elfhdr
-{
- uint32 magic; // Must equal ELF_MAGIC
- uint8 elf[12];
- uint16 type;
- uint16 machine;
- uint32 version;
- uint64 entry; // Program Entry Point (Virtual Address)
- uint64 phoff; // File offset of the Program Header Table
- uint64 shoff;
- uint32 flags;
- uint16 ehsize;
- uint16 phentsize;
- uint16 phnum; // Number of Program Headers
- uint16 shentsize;
- uint16 shnum;
- uint16 shstrndx;
-};
-
-// Program Header
-// Describes how to load segments from a file into memory
-struct proghdr
-{
- uint32 type;
- uint32 flags;
- uint64 off; // Offset of the section in the file
- uint64 vaddr; // Virtual address of the segment in memory
- uint64 paddr;
- uint64 filesz; // Size of the section in the file
- uint64 memsz; // The size of the segment in memory (memsz >= filesz)
- uint64 align;
-};
-
-// Values for Proghdr type
-#define ELF_PROG_LOAD 1
-
-// Flag bits for Proghdr flags
-#define ELF_PROG_FLAG_EXEC 1
-#define ELF_PROG_FLAG_WRITE 2
-#define ELF_PROG_FLAG_READ 4 \ No newline at end of file
diff --git a/include/kernel/exec.h b/include/kernel/exec.h
deleted file mode 100644
index 304e03f..0000000
--- a/include/kernel/exec.h
+++ /dev/null
@@ -1,11 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-
-namespace Exec
-{
- int exec(char *path, char **argv);
-} \ No newline at end of file
diff --git a/include/kernel/fdt.h b/include/kernel/fdt.h
deleted file mode 100644
index 89cbae6..0000000
--- a/include/kernel/fdt.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard Qin
- * LumeOS - FDT Device Discovery
- */
-
-#pragma once
-#include "common/types.h"
-
-// Single device descriptor (populated from FDT)
-struct FdtDevice {
- uint64 base_addr; // MMIO base address
- uint64 size; // MMIO region size
- uint32 irq; // Interrupt number (0 = none)
-};
-
-// Device registry — populated by fdt_parse(), consumed by drivers
-struct DeviceRegistry {
- // Memory
- uint64 mem_base;
- uint64 mem_size;
-
- // Core platform devices
- FdtDevice uart;
- FdtDevice plic;
- FdtDevice clint;
-
- // VirtIO devices (multiple possible)
- static constexpr int MAX_VIRTIO = 8;
- FdtDevice virtio[MAX_VIRTIO];
- int virtio_count;
-
- // MMIO ranges for kvminit() to map
- static constexpr int MAX_MMIO = 16;
- struct MmioRegion {
- uint64 base;
- uint64 size;
- } mmio_regions[MAX_MMIO];
- int mmio_count;
-};
-
-extern DeviceRegistry g_devices;
-
-// Parse FDT and populate g_devices
-void fdt_parse(uint64 dtb);
diff --git a/include/kernel/mm.h b/include/kernel/mm.h
deleted file mode 100644
index e3de09a..0000000
--- a/include/kernel/mm.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-namespace VM
-{
- extern uint64 *kernel_pagetable;
-
- void kvminit();
- void kvminithart();
-
- uint64 *uvmcreate();
- void uvminit(uint64 *pagetable, uchar *src, uint64 sz);
- uint64 uvmalloc(uint64 *pagetable, uint64 oldsz, uint64 newsz, int xperm);
- uint64 uvmdealloc(uint64 *pagetable, uint64 oldsz, uint64 newsz);
- void uvmfree(uint64 *pagetable, uint64 sz);
- int uvmcopy(uint64 *oldpt, uint64 *newpt, uint64 sz);
- void uvmunmap(uint64 *pagetable, uint64 va, uint64 npages, int do_free);
-
- int copyout(uint64 *pagetable, uint64 dstva, char *src, uint64 len);
- int copyin(uint64 *pagetable, char *dst, uint64 srcva, uint64 len);
- int copyinstr(uint64 *pagetable, char *dst, uint64 srcva, uint64 max);
-
- int handle_cow_fault(uint64 *pagetable, uint64 va);
- int mappages(uint64 *pagetable, uint64 va, uint64 size, uint64 pa, int perm);
-
- uint64 walkaddr(uint64 *pagetable, uint64 va);
-} \ No newline at end of file
diff --git a/include/kernel/pmm.h b/include/kernel/pmm.h
deleted file mode 100644
index 261786d..0000000
--- a/include/kernel/pmm.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard Qin
- */
-
-#pragma once
-#include "common/types.h"
-#include "kernel/spinlock.h"
-#include "kernel/fdt.h" // For g_devices
-
-// Physical Memory Layout Definition
-#define KERNBASE 0x80000000
-
-// Dynamic memory size from FDT
-// Default to 128MB if FDT not present/parsed
-extern uint64 g_phys_size;
-
-#define PHYSIZE (g_phys_size)
-#define PHYSTOP (KERNBASE + PHYSIZE)
-
-// Buddy System Max Order
-// Order 10 => 2^10 pages = 1024 * 4KB = 4MB contiguous block
-// Order 11 => 2^11 pages = 8MB
-#define MAX_ORDER 11
-
-// Page flag
-#define PG_used 0x01 // This page is occupied
-#define PG_reserved 0x02 // This page is reserved (kernel code/data, DTB, MemMap)
-
-// Physical Page Metadata Structure
-struct Page
-{
- uint8 flags; // Page Status (PG_used, PG_reserved)
- uint8 order; // If it is the first page of a free block, record the order of the current block
- uint16 refcnt; // Reference Counting
- struct Page *next; // Free list pointer (points to the next free block of the same size)
- struct Page *prev; // Doubly linked list (easy to remove)
-};
-
-namespace PMM
-{
- // Initialize physical memory management
- void init();
-
- // Core Allocation Interface (Buddy System)
- // order: 0 ~ MAX_ORDER-1
- void *alloc_pages(int order);
- void free_pages(void *pa, int order);
-
- // Compatible with old interface (allocate single page with order=0 and automatically reset to zero)
- void *alloc_page();
- void free_page(void *pa);
-
- // Helper function
- struct Page *pa_to_page(uint64 pa);
- uint64 page_to_pa(struct Page *page);
-
- // Debug information
- void print_free_lists();
-} \ No newline at end of file
diff --git a/include/kernel/proc.h b/include/kernel/proc.h
deleted file mode 100644
index e11dc47..0000000
--- a/include/kernel/proc.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-#include "kernel/proc_def.h"
-#include "kernel/spinlock.h"
-
-namespace ProcManager
-{
- void init();
- void scheduler();
- void yield();
-
- void create_kernel_thread(void (*func)(), const char *name);
-
- void user_init();
- void exit(int status);
- int wait(uint64 addr);
-
- void sleep(void *chan, Spinlock *lk);
- void wakeup(void *chan);
-
- int growproc(int n);
-
- int kill(int pid);
-}
-
-void forkret();
-struct Proc *myproc();
-void usertrapret();
-int fork(); \ No newline at end of file
diff --git a/include/kernel/proc_def.h b/include/kernel/proc_def.h
deleted file mode 100644
index 50f27d7..0000000
--- a/include/kernel/proc_def.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-#include "common/fcntl.h"
-
-struct cpu;
-struct Trapframe;
-class Inode;
-struct file;
-
-enum ProcState
-{
- UNUSED,
- USED,
- SLEEPING,
- RUNNABLE,
- RUNNING,
- ZOMBIE,
-};
-
-#define PRIO_MAX 0
-#define PRIO_MIN 3
-#define N_PRIO 4
-
-// Context structure
-struct Context
-{
- uint64 ra;
- uint64 sp;
- uint64 s0;
- uint64 s1;
- uint64 s2;
- uint64 s3;
- uint64 s4;
- uint64 s5;
- uint64 s6;
- uint64 s7;
- uint64 s8;
- uint64 s9;
- uint64 s10;
- uint64 s11;
-};
-
-struct Proc
-{
- struct Context context;
- struct Trapframe *tf;
- uint64 *pagetable;
- enum ProcState state;
- int pid;
- void *kstack;
- char name[16];
- uint64 sz;
- struct Proc *parent;
- void *chan;
- int xstate;
- int killed;
-
- Inode *cwd;
- struct file *ofile[NOFILE];
-
- struct Proc *next;
- struct Proc *prev;
-
- struct Proc *all_next;
- struct Proc *all_prev;
-
- int priority;
- int ticks;
- struct cpu *cpu;
-}; \ No newline at end of file
diff --git a/include/kernel/riscv.h b/include/kernel/riscv.h
deleted file mode 100644
index 0c586a9..0000000
--- a/include/kernel/riscv.h
+++ /dev/null
@@ -1,404 +0,0 @@
-#ifndef __ASSEMBLER__
-
-// which hart (core) is this?
-static inline uint64
-r_mhartid()
-{
- uint64 x;
- asm volatile("csrr %0, mhartid" : "=r" (x) );
- return x;
-}
-
-// Machine Status Register, mstatus
-
-#define MSTATUS_MPP_MASK (3L << 11) // previous mode.
-#define MSTATUS_MPP_M (3L << 11)
-#define MSTATUS_MPP_S (1L << 11)
-#define MSTATUS_MPP_U (0L << 11)
-
-static inline uint64
-r_mstatus()
-{
- uint64 x;
- asm volatile("csrr %0, mstatus" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_mstatus(uint64 x)
-{
- asm volatile("csrw mstatus, %0" : : "r" (x));
-}
-
-// machine exception program counter, holds the
-// instruction address to which a return from
-// exception will go.
-static inline void
-w_mepc(uint64 x)
-{
- asm volatile("csrw mepc, %0" : : "r" (x));
-}
-
-// Supervisor Status Register, sstatus
-
-#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User
-#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable
-#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable
-#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable
-#define SSTATUS_UIE (1L << 0) // User Interrupt Enable
-
-static inline uint64
-r_sstatus()
-{
- uint64 x;
- asm volatile("csrr %0, sstatus" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_sstatus(uint64 x)
-{
- asm volatile("csrw sstatus, %0" : : "r" (x));
-}
-
-// Supervisor Interrupt Pending
-static inline uint64
-r_sip()
-{
- uint64 x;
- asm volatile("csrr %0, sip" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_sip(uint64 x)
-{
- asm volatile("csrw sip, %0" : : "r" (x));
-}
-
-// Supervisor Interrupt Enable
-#define SIE_SEIE (1L << 9) // external
-#define SIE_STIE (1L << 5) // timer
-#define SIE_SSIE (1L << 1) // software
-static inline uint64
-r_sie()
-{
- uint64 x;
- asm volatile("csrr %0, sie" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_sie(uint64 x)
-{
- asm volatile("csrw sie, %0" : : "r" (x));
-}
-
-// Machine-mode Interrupt Enable
-#define MIE_STIE (1L << 5) // supervisor timer
-static inline uint64
-r_mie()
-{
- uint64 x;
- asm volatile("csrr %0, mie" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_mie(uint64 x)
-{
- asm volatile("csrw mie, %0" : : "r" (x));
-}
-
-// supervisor exception program counter, holds the
-// instruction address to which a return from
-// exception will go.
-static inline void
-w_sepc(uint64 x)
-{
- asm volatile("csrw sepc, %0" : : "r" (x));
-}
-
-static inline uint64
-r_sepc()
-{
- uint64 x;
- asm volatile("csrr %0, sepc" : "=r" (x) );
- return x;
-}
-
-// Machine Exception Delegation
-static inline uint64
-r_medeleg()
-{
- uint64 x;
- asm volatile("csrr %0, medeleg" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_medeleg(uint64 x)
-{
- asm volatile("csrw medeleg, %0" : : "r" (x));
-}
-
-// Machine Interrupt Delegation
-static inline uint64
-r_mideleg()
-{
- uint64 x;
- asm volatile("csrr %0, mideleg" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_mideleg(uint64 x)
-{
- asm volatile("csrw mideleg, %0" : : "r" (x));
-}
-
-// Supervisor Trap-Vector Base Address
-// low two bits are mode.
-static inline void
-w_stvec(uint64 x)
-{
- asm volatile("csrw stvec, %0" : : "r" (x));
-}
-
-static inline uint64
-r_stvec()
-{
- uint64 x;
- asm volatile("csrr %0, stvec" : "=r" (x) );
- return x;
-}
-
-// Supervisor Timer Comparison Register
-static inline uint64
-r_stimecmp()
-{
- uint64 x;
- // asm volatile("csrr %0, stimecmp" : "=r" (x) );
- asm volatile("csrr %0, 0x14d" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_stimecmp(uint64 x)
-{
- // asm volatile("csrw stimecmp, %0" : : "r" (x));
- asm volatile("csrw 0x14d, %0" : : "r" (x));
-}
-
-// Machine Environment Configuration Register
-static inline uint64
-r_menvcfg()
-{
- uint64 x;
- // asm volatile("csrr %0, menvcfg" : "=r" (x) );
- asm volatile("csrr %0, 0x30a" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_menvcfg(uint64 x)
-{
- //asm volatile("csrw menvcfg, %0" : : "r" (x));
- asm volatile("csrw 0x30a, %0" : : "r" (x));
-}
-
-// Physical Memory Protection
-static inline void
-w_pmpcfg0(uint64 x)
-{
- asm volatile("csrw pmpcfg0, %0" : : "r" (x));
-}
-
-static inline void
-w_pmpaddr0(uint64 x)
-{
- asm volatile("csrw pmpaddr0, %0" : : "r" (x));
-}
-
-// use riscv's sv39 page table scheme.
-#define SATP_SV39 (8L << 60)
-
-#define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12))
-
-// supervisor address translation and protection;
-// holds the address of the page table.
-static inline void
-w_satp(uint64 x)
-{
- asm volatile("csrw satp, %0" : : "r" (x));
-}
-
-static inline uint64
-r_satp()
-{
- uint64 x;
- asm volatile("csrr %0, satp" : "=r" (x) );
- return x;
-}
-
-// Supervisor Trap Cause
-static inline uint64
-r_scause()
-{
- uint64 x;
- asm volatile("csrr %0, scause" : "=r" (x) );
- return x;
-}
-
-// Supervisor Trap Value
-static inline uint64
-r_stval()
-{
- uint64 x;
- asm volatile("csrr %0, stval" : "=r" (x) );
- return x;
-}
-
-// Machine-mode Counter-Enable
-static inline void
-w_mcounteren(uint64 x)
-{
- asm volatile("csrw mcounteren, %0" : : "r" (x));
-}
-
-static inline uint64
-r_mcounteren()
-{
- uint64 x;
- asm volatile("csrr %0, mcounteren" : "=r" (x) );
- return x;
-}
-
-// machine-mode cycle counter
-static inline uint64
-r_time()
-{
- uint64 x;
- asm volatile("csrr %0, time" : "=r" (x) );
- return x;
-}
-
-// enable device interrupts
-static inline void
-intr_on()
-{
- w_sstatus(r_sstatus() | SSTATUS_SIE);
-}
-
-// disable device interrupts
-static inline void
-intr_off()
-{
- w_sstatus(r_sstatus() & ~SSTATUS_SIE);
-}
-
-// are device interrupts enabled?
-static inline int
-intr_get()
-{
- uint64 x = r_sstatus();
- return (x & SSTATUS_SIE) != 0;
-}
-
-static inline uint64
-r_sp()
-{
- uint64 x;
- asm volatile("mv %0, sp" : "=r" (x) );
- return x;
-}
-
-static inline uint64
-r_fp()
-{
- uint64 x;
- asm volatile("mv %0, s0" : "=r" (x) );
- return x;
-}
-
-// read and write tp, the thread pointer, which xv6 uses to hold
-// this core's hartid (core number), the index into cpus[].
-static inline uint64
-r_tp()
-{
- uint64 x;
- asm volatile("mv %0, tp" : "=r" (x) );
- return x;
-}
-
-static inline void
-w_tp(uint64 x)
-{
- asm volatile("mv tp, %0" : : "r" (x));
-}
-
-static inline uint64
-r_ra()
-{
- uint64 x;
- asm volatile("mv %0, ra" : "=r" (x) );
- return x;
-}
-
-// flush the TLB.
-static inline void
-sfence_vma()
-{
- // the zero, zero means flush all TLB entries.
- asm volatile("sfence.vma zero, zero");
-}
-
-typedef uint64 pte_t;
-typedef uint64 *pagetable_t; // 512 PTEs
-
-#endif // __ASSEMBLER__
-
-#define PGSIZE 4096 // bytes per page
-#define PGSHIFT 12 // bits of offset within a page
-
-#ifdef LAB_PGTBL
-#define SUPERPGSIZE (2 * (1 << 20)) // bytes per page
-#define SUPERPGROUNDUP(sz) (((sz)+SUPERPGSIZE-1) & ~(SUPERPGSIZE-1))
-#define SUPERPGROUNDDOWN(sz) (SUPERPGROUNDUP(sz)-SUPERPGSIZE)
-#endif
-
-#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
-#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
-
-#define PTE_V (1L << 0) // valid
-#define PTE_R (1L << 1)
-#define PTE_W (1L << 2)
-#define PTE_X (1L << 3)
-#define PTE_U (1L << 4) // user can access
-
-// COW Flag (PTE bit 8)
-#define PTE_COW (1L << 8)
-
-#if defined(LAB_MMAP) || defined(LAB_PGTBL) || defined(LAB_COW)
-#define PTE_LEAF(pte) (((pte) & PTE_R) | ((pte) & PTE_W) | ((pte) & PTE_X))
-#endif
-
-// shift a physical address to the right place for a PTE.
-#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
-
-#define PTE2PA(pte) (((pte) >> 10) << 12)
-
-#define PTE_FLAGS(pte) ((pte) & 0x3FF)
-
-// extract the three 9-bit page table indices from a virtual address.
-#define PXMASK 0x1FF // 9 bits
-#define PXSHIFT(level) (PGSHIFT+(9*(level)))
-#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK)
-
-// one beyond the highest possible virtual address.
-// MAXVA is actually one bit less than the max allowed by
-// Sv39, to avoid having to sign-extend virtual addresses
-// that have the high bit set.
-#define MAXVA (1L << (9 + 9 + 9 + 12 - 1))
-#define TRAMPOLINE (MAXVA - PGSIZE)
-#define TRAPFRAME (TRAMPOLINE - PGSIZE) \ No newline at end of file
diff --git a/include/kernel/runqueue.h b/include/kernel/runqueue.h
deleted file mode 100644
index 14704ce..0000000
--- a/include/kernel/runqueue.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "kernel/spinlock.h"
-#include "kernel/proc_def.h"
-
-struct RunQueue
-{
- Spinlock lock;
- struct Proc *heads[N_PRIO];
- struct Proc *tails[N_PRIO];
- uint64 count;
-
- void init(const char *name);
- void enqueue(struct Proc *p);
- struct Proc *dequeue();
- struct Proc *steal();
-}; \ No newline at end of file
diff --git a/include/kernel/sbi.h b/include/kernel/sbi.h
deleted file mode 100644
index a6be5b3..0000000
--- a/include/kernel/sbi.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-// SBI Extension IDs
-#define SBI_EID_SRST 0x53525354
-
-// SBI Function IDs for SRST
-#define SBI_FID_SRST_RESET 0
-
-// Reset Types
-#define SBI_SRST_TYPE_SHUTDOWN 0
-#define SBI_SRST_TYPE_COLD_REBOOT 1
-#define SBI_SRST_TYPE_WARM_REBOOT 2
-
-// Reset Reasons
-#define SBI_SRST_REASON_NONE 0
-#define SBI_SRST_REASON_SYSTEM_FAILURE 1
-
-namespace SBI
-{
- void set_timer(uint64 stime_value);
- void sbi_shutdown();
- void sbi_reboot();
-} // namespace SBI
diff --git a/include/kernel/slab.h b/include/kernel/slab.h
deleted file mode 100644
index bb7b699..0000000
--- a/include/kernel/slab.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-namespace Slab
-{
- void init();
-
- void *kmalloc(uint64 size);
- void kfree(void *ptr);
-} \ No newline at end of file
diff --git a/include/kernel/spinlock.h b/include/kernel/spinlock.h
deleted file mode 100644
index 1511199..0000000
--- a/include/kernel/spinlock.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-struct cpu;
-
-// Spinlock
-// Features: Non-interruptible, busy-wait loop, extremely short hold time.
-// Scenario: Protecting core data such as scheduler, interrupt handling, PMM, etc.
-class Spinlock
-{
-public:
- volatile uint32 locked; // Whether it is locked (0: unlocked, 1: locked)
- const char *name; // Lock Name (for debugging)
- struct cpu *cpu; // The CPU holding the lock
-
- // Constructor
- Spinlock() : locked(0), name("uninit"), cpu(nullptr) {}
-
- // Initialize lock name
- void init(const char *name);
-
- // Acquire lock
- void acquire();
-
- // Release lock
- void release();
-
- // Check if the current CPU holds the lock
- bool holding();
-};
-
-// Mutex
-// Features: Allows interruption, allows sleeping (yield CPU), can hold for a long time.
-// Scenarios: File system IO, device driver waiting, long-term buffering operations.
-class Mutex
-{
-public:
- uint32 locked; // Whether it is locked
- const char *name; // Lock Name
- int pid; // PID of the process holding the lock (for debugging)
-
- Spinlock lk; // Internal spin lock, protects the state of the Mutex itself
-
- void init(const char *name);
- void acquire();
- void release();
- bool holding();
-}; \ No newline at end of file
diff --git a/include/kernel/sync.h b/include/kernel/sync.h
deleted file mode 100644
index 7f7c208..0000000
--- a/include/kernel/sync.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-#include "kernel/spinlock.h"
-#include "kernel/proc_def.h"
-
-
-struct WaitQueue
-{
- Spinlock lock;
- struct Proc *head;
- struct Proc *tail;
-
- void init(const char *name);
- void sleep();
- void wakeup();
- void wakeup_all();
-};
-
-struct Semaphore
-{
- int value;
- WaitQueue wq;
- Spinlock lock;
-
- void init(int val, const char *name);
- void wait();
- void post();
-};
-
-struct CondVar
-{
- WaitQueue wq;
-
- void init(const char *name);
- void wait(Spinlock *mutex_lock);
- void signal();
- void broadcast();
-}; \ No newline at end of file
diff --git a/include/kernel/syscall.h b/include/kernel/syscall.h
deleted file mode 100644
index 3594c08..0000000
--- a/include/kernel/syscall.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard Qin
- * Lume OS - Linux Compatibility Layer
- *
- * Strictly aligned with Linux RISC-V 64 ABI (asm-generic)
- * Reference: linux/usr/include/asm-generic/unistd.h
- */
-
-#pragma once
-
-// File System
-#define SYS_getcwd 17
-#define SYS_dup 23
-#define SYS_dup3 24
-#define SYS_fcntl 25
-#define SYS_ioctl 29
-#define SYS_mknodat 33
-#define SYS_mkdirat 34
-#define SYS_unlinkat 35
-#define SYS_linkat 37
-#define SYS_umount2 39
-#define SYS_mount 40
-#define SYS_statfs 43
-#define SYS_ftruncate 46
-#define SYS_faccessat 48
-#define SYS_chdir 49
-#define SYS_fchmod 52
-#define SYS_fchmodat 53
-#define SYS_fchownat 54
-#define SYS_openat 56
-#define SYS_close 57
-#define SYS_vhangup 58
-#define SYS_pipe2 59
-#define SYS_getdents64 61
-#define SYS_lseek 62
-#define SYS_read 63
-#define SYS_write 64
-#define SYS_readv 65
-#define SYS_writev 66
-#define SYS_pread64 67
-#define SYS_pwrite64 68
-#define SYS_pselect6 72
-#define SYS_ppoll 73
-#define SYS_readlinkat 78
-#define SYS_fstatat 79 // newfstatat
-#define SYS_fstat 80
-
-// Process Management
-#define SYS_exit 93
-#define SYS_exit_group 94
-#define SYS_set_tid_address 96
-#define SYS_nanosleep 101
-#define SYS_kill 129
-#define SYS_rt_sigaction 134
-#define SYS_times 153
-#define SYS_uname 160
-#define SYS_gettimeofday 169
-#define SYS_getpid 172
-#define SYS_getppid 173
-#define SYS_getuid 174
-#define SYS_geteuid 175
-#define SYS_getgid 176
-#define SYS_getegid 177
-#define SYS_sysinfo 179
-#define SYS_brk 214
-#define SYS_munmap 215
-#define SYS_mremap 216
-#define SYS_clone 220
-#define SYS_execve 221
-#define SYS_mmap 222
-#define SYS_wait4 260
-#define SYS_prlimit64 261
-
-// System
-#define SYS_reboot 142 // Used for shutdown
-
-// === Custom / Debug (Non-Standard) ===
-// Placed above 1000 to avoid conflicts with Linux ABI
-#define SYS_putc 1000
-#define SYS_disk_test 1001
-
-#ifndef __ASSEMBLER__
-void syscall();
-#endif \ No newline at end of file
diff --git a/include/kernel/timer.h b/include/kernel/timer.h
deleted file mode 100644
index 8b97bbe..0000000
--- a/include/kernel/timer.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#include "common/types.h"
-
-namespace Timer{
- uint64 get_time();
- void set_next_trigger();
- void init();
-
- void tick();
- uint64 get_ticks();
- Spinlock *get_lock();
- void *get_tick_chan();
-} \ No newline at end of file
diff --git a/include/kernel/trap.h b/include/kernel/trap.h
deleted file mode 100644
index ddc1d12..0000000
--- a/include/kernel/trap.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-struct Trapframe
-{
- /* 0 */ uint64 kernel_satp;
- /* 8 */ uint64 kernel_sp;
- /* 16 */ uint64 kernel_trap;
- /* 24 */ uint64 epc;
- /* 32 */ uint64 kernel_hartid;
-
- /* 40 */ uint64 ra; // x1
- /* 48 */ uint64 sp; // x2
- /* 56 */ uint64 gp; // x3
- /* 64 */ uint64 tp; // x4
- /* 72 */ uint64 t0; // x5
- /* 80 */ uint64 t1; // x6
- /* 88 */ uint64 t2; // x7
- /* 96 */ uint64 s0; // x8 (Frame Pointer)
- /* 104 */ uint64 s1; // x9
- /* 112 */ uint64 a0; // x10
- /* 120 */ uint64 a1; // x11
- /* 128 */ uint64 a2; // x12
- /* 136 */ uint64 a3; // x13
- /* 144 */ uint64 a4; // x14
- /* 152 */ uint64 a5; // x15
- /* 160 */ uint64 a6; // x16
- /* 168 */ uint64 a7; // x17
- /* 176 */ uint64 s2; // x18
- /* 184 */ uint64 s3; // x19
- /* 192 */ uint64 s4; // x20
- /* 200 */ uint64 s5; // x21
- /* 208 */ uint64 s6; // x22
- /* 216 */ uint64 s7; // x23
- /* 224 */ uint64 s8; // x24
- /* 232 */ uint64 s9; // x25
- /* 240 */ uint64 s10; // x26
- /* 248 */ uint64 s11; // x27
- /* 256 */ uint64 t3; // x28
- /* 264 */ uint64 t4; // x29
- /* 272 */ uint64 t5; // x30
- /* 280 */ uint64 t6; // x31
-};
-
-namespace Trap
-{
- void init();
- void inithart();
- int devintr();
-} \ No newline at end of file
diff --git a/include/lib/errno.h b/include/lib/errno.h
deleted file mode 100644
index f0c1436..0000000
--- a/include/lib/errno.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * Lume OS - Linux Compatibility Layer
- *
- * This file aligns strictly with Linux RISC-V 64 ABI (asm-generic).
- * The definitions are copied from the Linux kernel's UAPI headers
- * (include/uapi/asm-generic/errno-base.h and include/uapi/asm-generic/errno.h)
- * to ensure ABI compatibility. Original copyrights apply.
- */
-
-#ifndef _ERRNO_H
-#define _ERRNO_H
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/* 1-34: asm-generic/errno-base.h */
-#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 of func */
-#define ERANGE 34 /* Math result not representable */
-
-/* * CRITICAL FIX: Missing definitions (35-37) from asm-generic
- * Without these, your file system and locking code will fail compliance.
- */
-#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 /* Can not 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 /* Attempting to link 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 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 */
-#define ENOTRECOVERABLE 131 /* State not recoverable */
-#define ERFKILL 132 /* Operation not possible due to RF-kill */
-#define EHWPOISON 133 /* Memory page has hardware error */
-
-extern __thread int errno;
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif \ No newline at end of file
diff --git a/include/lib/string.h b/include/lib/string.h
deleted file mode 100644
index 6a74942..0000000
--- a/include/lib/string.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#pragma once
-#include "common/types.h"
-
-
-void *memset(void *dst, int c, uint n);
-int memcmp(const void *v1, const void *v2, uint n);
-void *memmove(void *dst, const void *src, uint n);
-void *memcpy(void *dst, const void *src, uint n);
-
-
-void strcpy(char *dst, const char *src);
-int strlen(const char *s);
-int strcmp(const char *s1, const char *s2);
-int strcasecmp(const char *s1, const char *s2); \ No newline at end of file
diff --git a/include/libc-shim/bits/alltypes.h b/include/libc-shim/bits/alltypes.h
new file mode 100644
index 0000000..cf21a5e
--- /dev/null
+++ b/include/libc-shim/bits/alltypes.h
@@ -0,0 +1,15 @@
+/* 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/include/libc-shim/endian.h b/include/libc-shim/endian.h
new file mode 100644
index 0000000..8346386
--- /dev/null
+++ b/include/libc-shim/endian.h
@@ -0,0 +1,9 @@
+/* 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/include/libc-shim/features.h b/include/libc-shim/features.h
new file mode 100644
index 0000000..a3466c8
--- /dev/null
+++ b/include/libc-shim/features.h
@@ -0,0 +1,12 @@
+/* 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/include/libc-shim/limits.h b/include/libc-shim/limits.h
new file mode 100644
index 0000000..a1d546e
--- /dev/null
+++ b/include/libc-shim/limits.h
@@ -0,0 +1,17 @@
+/* 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/include/libc-shim/stdint.h b/include/libc-shim/stdint.h
new file mode 100644
index 0000000..650bdcf
--- /dev/null
+++ b/include/libc-shim/stdint.h
@@ -0,0 +1,21 @@
+/* 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/include/libc-shim/string.h b/include/libc-shim/string.h
new file mode 100644
index 0000000..8a86304
--- /dev/null
+++ b/include/libc-shim/string.h
@@ -0,0 +1,110 @@
+#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/include/libc-shim/strings.h b/include/libc-shim/strings.h
new file mode 100644
index 0000000..b7a5ea0
--- /dev/null
+++ b/include/libc-shim/strings.h
@@ -0,0 +1,40 @@
+#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/include/lume/addr.h b/include/lume/addr.h
new file mode 100644
index 0000000..572b6fe
--- /dev/null
+++ b/include/lume/addr.h
@@ -0,0 +1,91 @@
+/* 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/include/lume/addr_types.h b/include/lume/addr_types.h
new file mode 100644
index 0000000..91a0779
--- /dev/null
+++ b/include/lume/addr_types.h
@@ -0,0 +1,29 @@
+/* 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/include/lume/args.h b/include/lume/args.h
new file mode 100644
index 0000000..0562dc5
--- /dev/null
+++ b/include/lume/args.h
@@ -0,0 +1,28 @@
+/* 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/include/lume/atomic.h b/include/lume/atomic.h
new file mode 100644
index 0000000..6e31129
--- /dev/null
+++ b/include/lume/atomic.h
@@ -0,0 +1,76 @@
+/* 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/include/lume/bitops.h b/include/lume/bitops.h
new file mode 100644
index 0000000..4227ca3
--- /dev/null
+++ b/include/lume/bitops.h
@@ -0,0 +1,120 @@
+/* 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/include/lume/chardev.h b/include/lume/chardev.h
new file mode 100644
index 0000000..be81ab9
--- /dev/null
+++ b/include/lume/chardev.h
@@ -0,0 +1,45 @@
+/* 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/include/lume/clone.h b/include/lume/clone.h
new file mode 100644
index 0000000..156bae4
--- /dev/null
+++ b/include/lume/clone.h
@@ -0,0 +1,34 @@
+/* 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/include/lume/compiler.h b/include/lume/compiler.h
new file mode 100644
index 0000000..243f0de
--- /dev/null
+++ b/include/lume/compiler.h
@@ -0,0 +1,54 @@
+/* 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/include/lume/config.h b/include/lume/config.h
new file mode 100644
index 0000000..bde717a
--- /dev/null
+++ b/include/lume/config.h
@@ -0,0 +1,100 @@
+/* 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/include/lume/console.h b/include/lume/console.h
new file mode 100644
index 0000000..af187a6
--- /dev/null
+++ b/include/lume/console.h
@@ -0,0 +1,77 @@
+/* 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/include/lume/container_of.h b/include/lume/container_of.h
new file mode 100644
index 0000000..a6889eb
--- /dev/null
+++ b/include/lume/container_of.h
@@ -0,0 +1,17 @@
+/* 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/include/lume/dcache.h b/include/lume/dcache.h
new file mode 100644
index 0000000..a31402e
--- /dev/null
+++ b/include/lume/dcache.h
@@ -0,0 +1,143 @@
+/* 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/include/lume/device.h b/include/lume/device.h
new file mode 100644
index 0000000..46e22e4
--- /dev/null
+++ b/include/lume/device.h
@@ -0,0 +1,75 @@
+/* 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/include/lume/device_node.h b/include/lume/device_node.h
new file mode 100644
index 0000000..e35f22d
--- /dev/null
+++ b/include/lume/device_node.h
@@ -0,0 +1,64 @@
+/* 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/include/lume/driver.h b/include/lume/driver.h
new file mode 100644
index 0000000..a8bd787
--- /dev/null
+++ b/include/lume/driver.h
@@ -0,0 +1,24 @@
+/* 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/include/lume/elf.h b/include/lume/elf.h
new file mode 100644
index 0000000..772ad97
--- /dev/null
+++ b/include/lume/elf.h
@@ -0,0 +1,150 @@
+/* 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/include/lume/errno.h b/include/lume/errno.h
new file mode 100644
index 0000000..f3bf09f
--- /dev/null
+++ b/include/lume/errno.h
@@ -0,0 +1,186 @@
+/* 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/include/lume/fdt.h b/include/lume/fdt.h
new file mode 100644
index 0000000..626cc48
--- /dev/null
+++ b/include/lume/fdt.h
@@ -0,0 +1,85 @@
+/* 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/include/lume/fdt_prop.h b/include/lume/fdt_prop.h
new file mode 100644
index 0000000..f8a263c
--- /dev/null
+++ b/include/lume/fdt_prop.h
@@ -0,0 +1,24 @@
+/* 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/include/lume/fdtable.h b/include/lume/fdtable.h
new file mode 100644
index 0000000..6159519
--- /dev/null
+++ b/include/lume/fdtable.h
@@ -0,0 +1,49 @@
+/* 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/include/lume/features.h b/include/lume/features.h
new file mode 100644
index 0000000..85cfb72
--- /dev/null
+++ b/include/lume/features.h
@@ -0,0 +1,40 @@
+#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/include/lume/file.h b/include/lume/file.h
new file mode 100644
index 0000000..349059b
--- /dev/null
+++ b/include/lume/file.h
@@ -0,0 +1,132 @@
+/* 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/include/lume/fs.h b/include/lume/fs.h
new file mode 100644
index 0000000..8dac552
--- /dev/null
+++ b/include/lume/fs.h
@@ -0,0 +1,310 @@
+/* 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/include/lume/fs_struct.h b/include/lume/fs_struct.h
new file mode 100644
index 0000000..ba643b5
--- /dev/null
+++ b/include/lume/fs_struct.h
@@ -0,0 +1,39 @@
+/* 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/include/lume/fs_types.h b/include/lume/fs_types.h
new file mode 100644
index 0000000..767f37c
--- /dev/null
+++ b/include/lume/fs_types.h
@@ -0,0 +1,197 @@
+/* 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/include/lume/init.h b/include/lume/init.h
new file mode 100644
index 0000000..541ab96
--- /dev/null
+++ b/include/lume/init.h
@@ -0,0 +1,33 @@
+/* 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/include/lume/irq.h b/include/lume/irq.h
new file mode 100644
index 0000000..f5f34e4
--- /dev/null
+++ b/include/lume/irq.h
@@ -0,0 +1,84 @@
+/* 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/include/lume/kbuild.h b/include/lume/kbuild.h
new file mode 100644
index 0000000..4c3e12a
--- /dev/null
+++ b/include/lume/kbuild.h
@@ -0,0 +1,40 @@
+/* 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/include/lume/kernel_map.h b/include/lume/kernel_map.h
new file mode 100644
index 0000000..7eafe88
--- /dev/null
+++ b/include/lume/kernel_map.h
@@ -0,0 +1,55 @@
+/* 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/include/lume/klog.h b/include/lume/klog.h
new file mode 100644
index 0000000..69ab269
--- /dev/null
+++ b/include/lume/klog.h
@@ -0,0 +1,200 @@
+/* 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/include/lume/kobject.h b/include/lume/kobject.h
new file mode 100644
index 0000000..8581d6e
--- /dev/null
+++ b/include/lume/kobject.h
@@ -0,0 +1,57 @@
+/* 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/include/lume/kprintf.h b/include/lume/kprintf.h
new file mode 100644
index 0000000..b158602
--- /dev/null
+++ b/include/lume/kprintf.h
@@ -0,0 +1,39 @@
+/* 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/include/lume/kref.h b/include/lume/kref.h
new file mode 100644
index 0000000..13d53f3
--- /dev/null
+++ b/include/lume/kref.h
@@ -0,0 +1,46 @@
+/* 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/include/lume/libfdt.h b/include/lume/libfdt.h
new file mode 100644
index 0000000..e3496e9
--- /dev/null
+++ b/include/lume/libfdt.h
@@ -0,0 +1,8 @@
+/* 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/include/lume/libfdt_env.h b/include/lume/libfdt_env.h
new file mode 100644
index 0000000..a95fb1d
--- /dev/null
+++ b/include/lume/libfdt_env.h
@@ -0,0 +1,78 @@
+/* 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/include/lume/limits.h b/include/lume/limits.h
new file mode 100644
index 0000000..a275a71
--- /dev/null
+++ b/include/lume/limits.h
@@ -0,0 +1,48 @@
+/* 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/include/lume/list.h b/include/lume/list.h
new file mode 100644
index 0000000..ed1202a
--- /dev/null
+++ b/include/lume/list.h
@@ -0,0 +1,346 @@
+/* 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/include/lume/lumeos.lds.h b/include/lume/lumeos.lds.h
new file mode 100644
index 0000000..58d2f72
--- /dev/null
+++ b/include/lume/lumeos.lds.h
@@ -0,0 +1,193 @@
+/* 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/include/lume/memblock.h b/include/lume/memblock.h
new file mode 100644
index 0000000..cb9a76b
--- /dev/null
+++ b/include/lume/memblock.h
@@ -0,0 +1,53 @@
+/* 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/include/lume/mm_layout.h b/include/lume/mm_layout.h
new file mode 100644
index 0000000..7744de0
--- /dev/null
+++ b/include/lume/mm_layout.h
@@ -0,0 +1,73 @@
+/* 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/include/lume/mman.h b/include/lume/mman.h
new file mode 100644
index 0000000..d952419
--- /dev/null
+++ b/include/lume/mman.h
@@ -0,0 +1,21 @@
+/* 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/include/lume/mount.h b/include/lume/mount.h
new file mode 100644
index 0000000..13f8e62
--- /dev/null
+++ b/include/lume/mount.h
@@ -0,0 +1,52 @@
+/* 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/include/lume/mutex.h b/include/lume/mutex.h
new file mode 100644
index 0000000..9719aa6
--- /dev/null
+++ b/include/lume/mutex.h
@@ -0,0 +1,63 @@
+/* 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/include/lume/namei.h b/include/lume/namei.h
new file mode 100644
index 0000000..1764693
--- /dev/null
+++ b/include/lume/namei.h
@@ -0,0 +1,32 @@
+/* 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/include/lume/new.h b/include/lume/new.h
new file mode 100644
index 0000000..cdcacb8
--- /dev/null
+++ b/include/lume/new.h
@@ -0,0 +1,18 @@
+/* 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/include/lume/page.h b/include/lume/page.h
new file mode 100644
index 0000000..61bff95
--- /dev/null
+++ b/include/lume/page.h
@@ -0,0 +1,151 @@
+/* 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/include/lume/page_cache.h b/include/lume/page_cache.h
new file mode 100644
index 0000000..abdc535
--- /dev/null
+++ b/include/lume/page_cache.h
@@ -0,0 +1,82 @@
+/* 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/include/lume/pgrp.h b/include/lume/pgrp.h
new file mode 100644
index 0000000..127f3f8
--- /dev/null
+++ b/include/lume/pgrp.h
@@ -0,0 +1,125 @@
+/* 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/include/lume/pgtable_types.h b/include/lume/pgtable_types.h
new file mode 100644
index 0000000..e309a82
--- /dev/null
+++ b/include/lume/pgtable_types.h
@@ -0,0 +1,69 @@
+/* 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/include/lume/pid.h b/include/lume/pid.h
new file mode 100644
index 0000000..8503750
--- /dev/null
+++ b/include/lume/pid.h
@@ -0,0 +1,29 @@
+/* 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/include/lume/pipe.h b/include/lume/pipe.h
new file mode 100644
index 0000000..c905c9a
--- /dev/null
+++ b/include/lume/pipe.h
@@ -0,0 +1,52 @@
+/* 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/include/lume/platform_device.h b/include/lume/platform_device.h
new file mode 100644
index 0000000..ac3e527
--- /dev/null
+++ b/include/lume/platform_device.h
@@ -0,0 +1,64 @@
+/* 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/include/lume/pmm.h b/include/lume/pmm.h
new file mode 100644
index 0000000..3e4b819
--- /dev/null
+++ b/include/lume/pmm.h
@@ -0,0 +1,108 @@
+/* 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/include/lume/poll.h b/include/lume/poll.h
new file mode 100644
index 0000000..7d5bca2
--- /dev/null
+++ b/include/lume/poll.h
@@ -0,0 +1,68 @@
+/* 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/include/lume/ptrace.h b/include/lume/ptrace.h
new file mode 100644
index 0000000..687d103
--- /dev/null
+++ b/include/lume/ptrace.h
@@ -0,0 +1,76 @@
+/* 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/include/lume/random.h b/include/lume/random.h
new file mode 100644
index 0000000..740b060
--- /dev/null
+++ b/include/lume/random.h
@@ -0,0 +1,38 @@
+/* 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/include/lume/rbtree.h b/include/lume/rbtree.h
new file mode 100644
index 0000000..80b0c6c
--- /dev/null
+++ b/include/lume/rbtree.h
@@ -0,0 +1,207 @@
+/* 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/include/lume/rbtree_augmented.h b/include/lume/rbtree_augmented.h
new file mode 100644
index 0000000..d4559c4
--- /dev/null
+++ b/include/lume/rbtree_augmented.h
@@ -0,0 +1,252 @@
+/* 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/include/lume/rbtree_types.h b/include/lume/rbtree_types.h
new file mode 100644
index 0000000..688ade4
--- /dev/null
+++ b/include/lume/rbtree_types.h
@@ -0,0 +1,28 @@
+/* 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/include/lume/sched.h b/include/lume/sched.h
new file mode 100644
index 0000000..ab26bac
--- /dev/null
+++ b/include/lume/sched.h
@@ -0,0 +1,102 @@
+/* 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/include/lume/sched_class.h b/include/lume/sched_class.h
new file mode 100644
index 0000000..08686c1
--- /dev/null
+++ b/include/lume/sched_class.h
@@ -0,0 +1,35 @@
+/* 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/include/lume/selftest.h b/include/lume/selftest.h
new file mode 100644
index 0000000..7040986
--- /dev/null
+++ b/include/lume/selftest.h
@@ -0,0 +1,65 @@
+/* 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/include/lume/setup.h b/include/lume/setup.h
new file mode 100644
index 0000000..f8e6cb6
--- /dev/null
+++ b/include/lume/setup.h
@@ -0,0 +1,42 @@
+/* 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/include/lume/signal.h b/include/lume/signal.h
new file mode 100644
index 0000000..05b7542
--- /dev/null
+++ b/include/lume/signal.h
@@ -0,0 +1,60 @@
+/* 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/include/lume/signal_nr.h b/include/lume/signal_nr.h
new file mode 100644
index 0000000..8e595d7
--- /dev/null
+++ b/include/lume/signal_nr.h
@@ -0,0 +1,54 @@
+/* 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/include/lume/signal_types.h b/include/lume/signal_types.h
new file mode 100644
index 0000000..f66231b
--- /dev/null
+++ b/include/lume/signal_types.h
@@ -0,0 +1,263 @@
+/* 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/include/lume/slab.h b/include/lume/slab.h
new file mode 100644
index 0000000..4ebade1
--- /dev/null
+++ b/include/lume/slab.h
@@ -0,0 +1,28 @@
+/* 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/include/lume/smp.h b/include/lume/smp.h
new file mode 100644
index 0000000..a5292b6
--- /dev/null
+++ b/include/lume/smp.h
@@ -0,0 +1,74 @@
+/* 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/include/lume/stdarg.h b/include/lume/stdarg.h
new file mode 100644
index 0000000..c8dc7f4
--- /dev/null
+++ b/include/lume/stdarg.h
@@ -0,0 +1,11 @@
+// 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/include/lume/stringify.h b/include/lume/stringify.h
new file mode 100644
index 0000000..0e84cbe
--- /dev/null
+++ b/include/lume/stringify.h
@@ -0,0 +1,14 @@
+#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/include/lume/syscall.h b/include/lume/syscall.h
new file mode 100644
index 0000000..8185096
--- /dev/null
+++ b/include/lume/syscall.h
@@ -0,0 +1,38 @@
+/* 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/include/lume/syscall_nr.h b/include/lume/syscall_nr.h
new file mode 100644
index 0000000..29e3904
--- /dev/null
+++ b/include/lume/syscall_nr.h
@@ -0,0 +1,82 @@
+/* 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/include/lume/task.h b/include/lume/task.h
new file mode 100644
index 0000000..e959147
--- /dev/null
+++ b/include/lume/task.h
@@ -0,0 +1,144 @@
+/* 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/include/lume/timer.h b/include/lume/timer.h
new file mode 100644
index 0000000..9bdddd5
--- /dev/null
+++ b/include/lume/timer.h
@@ -0,0 +1,43 @@
+/* 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/include/lume/tlb.h b/include/lume/tlb.h
new file mode 100644
index 0000000..b4d51ea
--- /dev/null
+++ b/include/lume/tlb.h
@@ -0,0 +1,48 @@
+/* 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/include/lume/trap.h b/include/lume/trap.h
new file mode 100644
index 0000000..61a5054
--- /dev/null
+++ b/include/lume/trap.h
@@ -0,0 +1,109 @@
+/* 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/include/lume/tty.h b/include/lume/tty.h
new file mode 100644
index 0000000..34cf7bb
--- /dev/null
+++ b/include/lume/tty.h
@@ -0,0 +1,165 @@
+/* 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/include/lume/tty_ioctl.h b/include/lume/tty_ioctl.h
new file mode 100644
index 0000000..87ba59b
--- /dev/null
+++ b/include/lume/tty_ioctl.h
@@ -0,0 +1,202 @@
+/* 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/include/lume/types.h b/include/lume/types.h
new file mode 100644
index 0000000..2342359
--- /dev/null
+++ b/include/lume/types.h
@@ -0,0 +1,78 @@
+/* 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/include/lume/uaccess.h b/include/lume/uaccess.h
new file mode 100644
index 0000000..5a9ed71
--- /dev/null
+++ b/include/lume/uaccess.h
@@ -0,0 +1,27 @@
+/* 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/include/lume/uuid.h b/include/lume/uuid.h
new file mode 100644
index 0000000..fcbd4a3
--- /dev/null
+++ b/include/lume/uuid.h
@@ -0,0 +1,207 @@
+/* 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/include/lume/vmm.h b/include/lume/vmm.h
new file mode 100644
index 0000000..9cbebf1
--- /dev/null
+++ b/include/lume/vmm.h
@@ -0,0 +1,115 @@
+/* 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/include/lume/wait.h b/include/lume/wait.h
new file mode 100644
index 0000000..865c1f2
--- /dev/null
+++ b/include/lume/wait.h
@@ -0,0 +1,31 @@
+/* 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/include/lume/waitqueue.h b/include/lume/waitqueue.h
new file mode 100644
index 0000000..3720cb7
--- /dev/null
+++ b/include/lume/waitqueue.h
@@ -0,0 +1,79 @@
+/* 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/include/ulib/assert.h b/include/ulib/assert.h
deleted file mode 100644
index 014be9b..0000000
--- a/include/ulib/assert.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#ifndef _ASSERT_H
-#define _ASSERT_H
-
-#ifdef NDEBUG
-#define assert(ignore) ((void)0)
-#else
-#ifdef __cplusplus
-extern "C"
-{
-#endif
- void __assert_fail(const char *expr, const char *file, int line, const char *func);
-#ifdef __cplusplus
-}
-#endif
-
-#define assert(expr) \
- ((expr) ? (void)0 : __assert_fail(#expr, __FILE__, __LINE__, __func__))
-#endif
-
-#endif \ No newline at end of file
diff --git a/include/ulib/cstdlib b/include/ulib/cstdlib
deleted file mode 100644
index 8b07338..0000000
--- a/include/ulib/cstdlib
+++ /dev/null
@@ -1,57 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#ifndef _CPP_CSTDLIB
-#define _CPP_CSTDLIB
-
-#include "stdlib.h"
-
-namespace std
-{
- using :: malloc;
- using :: free;
- using :: calloc;
- using :: realloc;
-
- using :: exit;
- using :: abort;
- using :: atexit;
- using :: getenv;
- using :: system;
-
- using :: atoi;
- using :: atol;
- using :: atoll;
- using :: atof;
-
- using :: strtol;
- using :: strtoul;
- using :: strtoll;
- using :: strtoull;
-
- using :: strtod;
- using :: strtof;
-
- using :: bsearch;
- using :: qsort;
-
- using :: abs;
- using :: labs;
- using :: llabs;
- using :: div;
- using :: ldiv;
- using :: lldiv;
-
- using :: rand;
- using :: srand;
-
- using :: mblen;
- using :: mbtowc;
- using :: wctomb;
- using :: mbstowcs;
- using :: wcstombs;
-}
-
-#endif \ No newline at end of file
diff --git a/include/ulib/cstring b/include/ulib/cstring
deleted file mode 100644
index d65dc8d..0000000
--- a/include/ulib/cstring
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#ifndef _CPP_CSTRING
-#define _CPP_CSTRING
-
-#include "string.h"
-
-namespace std
-{
- using::memchr;
- using::memcmp;
- using::memcpy;
- using::memmove;
- using::memset;
- using::strcat;
- using::strchr;
- using::strcmp;
- using::strcpy;
- using::strcspn;
- using::strdup;
- using::strerror;
- using::strlcat;
- using::strlcpy;
- using::strlen;
- using::strncat;
- using::strncmp;
- using::strpbrk;
- using::strrchr;
- using::strspn;
- using::strstr;
- using::strtok;
-} // namespace std
-
-#endif \ No newline at end of file
diff --git a/include/ulib/ctypes.h b/include/ulib/ctypes.h
deleted file mode 100644
index 1ec204e..0000000
--- a/include/ulib/ctypes.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#ifndef _CTYPE_H
-#define _CTYPE_H
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- static inline int isdigit(int c) { return c >= '0' && c <= '9'; }
- static inline int islower(int c) { return c >= 'a' && c <= 'z'; }
- static inline int isupper(int c) { return c >= 'A' && c <= 'Z'; }
- static inline int isalpha(int c) { return islower(c) || isupper(c); }
- static inline int isalnum(int c) { return isalpha(c) || isdigit(c); }
- static inline int isspace(int c) { return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; }
- static inline int isprint(int c) { return c >= 32 && c <= 126; }
- static inline int iscntrl(int c) { return c < 32 || c == 127; }
- static inline int isxdigit(int c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); }
-
- static inline int tolower(int c) { return isupper(c) ? c + 32 : c; }
- static inline int toupper(int c) { return islower(c) ? c - 32 : c; }
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif \ No newline at end of file
diff --git a/include/ulib/cxx.cc b/include/ulib/cxx.cc
deleted file mode 100644
index f454fea..0000000
--- a/include/ulib/cxx.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#include "ulib/stdlib.h"
-
-
-void *operator new(unsigned long n)
-{
- return malloc(n);
-}
-
-void *operator new[](unsigned long n)
-{
- return malloc(n);
-}
-
-void operator delete(void *p)
-{
- free(p);
-}
-
-void operator delete[](void *p)
-{
- free(p);
-}
-
-void operator delete(void *p, unsigned long)
-{
- free(p);
-}
-
-void operator delete[](void *p, unsigned long)
-{
- free(p);
-} \ No newline at end of file
diff --git a/include/ulib/errno.h b/include/ulib/errno.h
deleted file mode 100644
index dd51598..0000000
--- a/include/ulib/errno.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * Lume OS - Linux Compatibility Layer
- *
- * This file aligns strictly with Linux RISC-V 64 ABI (asm-generic).
- * The definitions are copied from the Linux kernel's UAPI headers
- * (include/uapi/asm-generic/errno-base.h and include/uapi/asm-generic/errno.h)
- * to ensure ABI compatibility. Original copyrights apply.
- */
-
-#ifndef _ERRNO_H
-#define _ERRNO_H
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#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 of func */
-#define ERANGE 34 /* Math result not representable */
-/*
- * This error code is special: arch syscall entry code will return
- * -ENOSYS if users try to call a syscall that doesn't exist. To keep
- * failures of syscalls that really do exist distinguishable from
- * failures due to attempts to use a nonexistent syscall, syscall
- * implementations should refrain from returning -ENOSYS.
- */
-#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 /* Can not 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 /* Attempting to link 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 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 */
-
-/* for robust mutexes */
-#define EOWNERDEAD 130 /* Owner died */
-#define ENOTRECOVERABLE 131 /* State not recoverable */
-
-#define ERFKILL 132 /* Operation not possible due to RF-kill */
-
-#define EHWPOISON 133 /* Memory page has hardware error */
-
-extern int errno;
-#ifdef __cplusplus
-}
-#endif
-
-#endif \ No newline at end of file
diff --git a/include/ulib/iostream b/include/ulib/iostream
deleted file mode 100644
index b05f603..0000000
--- a/include/ulib/iostream
+++ /dev/null
@@ -1,115 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#ifndef _IOSTREAM
-#define _IOSTREAM
-
-#include "stdio.h"
-
-namespace std
-{
- class ostream
- {
- private:
- FILE* _stream;
- public:
- ostream(FILE* f) : _stream(f) {}
-
- ostream& operator<<(const char* str)
- {
- fprintf(_stream, "%s", str);
- return *this;
- }
-
- ostream& operator<<(int n)
- {
- fprintf(_stream, "%d", n);
- return *this;
- }
-
- ostream &operator<<(long n)
- {
- fprintf(_stream, "%ld", n);
- return *this;
- }
-
- ostream &operator<<(long long n)
- {
- fprintf(_stream, "%lld", n);
- return *this;
- }
-
- ostream &operator<<(double f)
- {
- fprintf(_stream, "%f", f);
- return *this;
- }
-
- ostream &operator<<(char c)
- {
- fputc(c, _stream);
- return *this;
- }
-
- ostream &operator<<(void *p)
- {
- fprintf(_stream, "%p", p);
- return *this;
- }
-
- ostream &operator<<(ostream &(*func)(ostream &))
- {
- return func(*this);
- }
-
- ostream &flush()
- {
- fflush(_stream);
- return *this;
- }
- };
-
- class istream{
- private:
- FILE* _stream;
- public:
- istream(FILE *f) : _stream(f) {}
-
- istream& operator>>(int n)
- {
- fscanf(_stream, "%d", n);
- return *this;
- }
-
- istream &operator>>(double &f)
- {
- fscanf(_stream, "%lf", &f);
- return *this;
- }
-
- istream &operator>>(char *str)
- {
- fscanf(_stream, "%s", str);
- return *this;
- }
-
- istream &operator>>(char &c)
- {
- char tmp[2];
- fscanf(_stream, " %c", tmp);
- c = tmp[0];
- return *this;
- }
- };
-
- inline ostream &endl(ostream &os)
- {
- os << '\n';
- os.flush();
- return os;
- }
-}
-
-#endif \ No newline at end of file
diff --git a/include/ulib/limits.h b/include/ulib/limits.h
deleted file mode 100644
index b317e6a..0000000
--- a/include/ulib/limits.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-#pragma once
-
-#define CHAR_BIT 8
-#define SCHAR_MIN (-128)
-#define SCHAR_MAX 127
-#define UCHAR_MAX 255
-
-#define SHRT_MIN (-32768)
-#define SHRT_MAX 32767
-#define USHRT_MAX 65535
-
-#define INT_MIN (-2147483648)
-#define INT_MAX 2147483647
-#define UINT_MAX 4294967295U
-
-#define LONG_MAX 9223372036854775807L
-#define LONG_MIN (-LONG_MAX - 1L)
-#define ULONG_MAX 18446744073709551615UL
-
-#define LLONG_MAX 9223372036854775807LL
-#define LLONG_MIN (-LLONG_MAX - 1LL)
-#define ULLONG_MAX 18446744073709551615ULL \ No newline at end of file
diff --git a/include/ulib/stb_sprintf.h b/include/ulib/stb_sprintf.h
deleted file mode 100644
index 00dc123..0000000
--- a/include/ulib/stb_sprintf.h
+++ /dev/null
@@ -1,1916 +0,0 @@
-// stb_sprintf - v1.10 - public domain snprintf() implementation
-// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
-// http://github.com/nothings/stb
-//
-// allowed types: sc uidBboXx p AaGgEef n
-// lengths : hh h ll j z t I64 I32 I
-//
-// Contributors:
-// Fabian "ryg" Giesen (reformatting)
-// github:aganm (attribute format)
-//
-// Contributors (bugfixes):
-// github:d26435
-// github:trex78
-// github:account-login
-// Jari Komppa (SI suffixes)
-// Rohit Nirmal
-// Marcin Wojdyr
-// Leonard Ritter
-// Stefano Zanotti
-// Adam Allison
-// Arvid Gerstmann
-// Markus Kolb
-//
-// LICENSE:
-//
-// See end of file for license information.
-
-#ifndef STB_SPRINTF_H_INCLUDE
-#define STB_SPRINTF_H_INCLUDE
-
-/*
-Single file sprintf replacement.
-
-Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
-Hereby placed in public domain.
-
-This is a full sprintf replacement that supports everything that
-the C runtime sprintfs support, including float/double, 64-bit integers,
-hex floats, field parameters (%*.*d stuff), length reads backs, etc.
-
-Why would you need this if sprintf already exists? Well, first off,
-it's *much* faster (see below). It's also much smaller than the CRT
-versions code-space-wise. We've also added some simple improvements
-that are super handy (commas in thousands, callbacks at buffer full,
-for example). Finally, the format strings for MSVC and GCC differ
-for 64-bit integers (among other small things), so this lets you use
-the same format strings in cross platform code.
-
-It uses the standard single file trick of being both the header file
-and the source itself. If you just include it normally, you just get
-the header file function definitions. To get the code, you include
-it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
-
-It only uses va_args macros from the C runtime to do it's work. It
-does cast doubles to S64s and shifts and divides U64s, which does
-drag in CRT code on most platforms.
-
-It compiles to roughly 8K with float support, and 4K without.
-As a comparison, when using MSVC static libs, calling sprintf drags
-in 16K.
-
-API:
-====
-int stbsp_sprintf( char * buf, char const * fmt, ... )
-int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
- Convert an arg list into a buffer. stbsp_snprintf always returns
- a zero-terminated string (unlike regular snprintf).
-
-int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
-int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
- Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns
- a zero-terminated string (unlike regular snprintf).
-
-int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
- typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
- Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
- Your callback can then copy the chars out, print them or whatever.
- This function is actually the workhorse for everything else.
- The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
- // you return the next buffer to use or 0 to stop converting
-
-void stbsp_set_separators( char comma, char period )
- Set the comma and period characters to use.
-
-FLOATS/DOUBLES:
-===============
-This code uses a internal float->ascii conversion method that uses
-doubles with error correction (double-doubles, for ~105 bits of
-precision). This conversion is round-trip perfect - that is, an atof
-of the values output here will give you the bit-exact double back.
-
-One difference is that our insignificant digits will be different than
-with MSVC or GCC (but they don't match each other either). We also
-don't attempt to find the minimum length matching float (pre-MSVC15
-doesn't either).
-
-If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
-and you'll save 4K of code space.
-
-64-BIT INTS:
-============
-This library also supports 64-bit integers and you can use MSVC style or
-GCC style indicators (%I64d or %lld). It supports the C99 specifiers
-for size_t and ptr_diff_t (%jd %zd) as well.
-
-EXTRAS:
-=======
-Like some GCCs, for integers and floats, you can use a ' (single quote)
-specifier and commas will be inserted on the thousands: "%'d" on 12345
-would print 12,345.
-
-For integers and floats, you can use a "$" specifier and the number
-will be converted to float and then divided to get kilo, mega, giga or
-tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
-"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
-2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
-$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
-suffix, add "_" specifier: "%_$d" -> "2.53M".
-
-In addition to octal and hexadecimal conversions, you can print
-integers in binary: "%b" for 256 would print 100.
-
-PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
-===================================================================
-"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
-"%24d" across all 32-bit ints (4.5x/4.2x faster)
-"%x" across all 32-bit ints (4.5x/3.8x faster)
-"%08x" across all 32-bit ints (4.3x/3.8x faster)
-"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
-"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
-"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
-"%f" for values near e-300 (7.9x/6.5x faster)
-"%f" for values near e+300 (10.0x/9.1x faster)
-"%e" for values near e-300 (10.1x/7.0x faster)
-"%e" for values near e+300 (9.2x/6.0x faster)
-"%.320f" for values near e-300 (12.6x/11.2x faster)
-"%a" for random values (8.6x/4.3x faster)
-"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
-"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
-"%s%s%s" for 64 char strings (7.1x/7.3x faster)
-"...512 char string..." ( 35.0x/32.5x faster!)
-*/
-
-#if defined(__clang__)
- #if defined(__has_feature) && defined(__has_attribute)
- #if __has_feature(address_sanitizer)
- #if __has_attribute(__no_sanitize__)
- #define STBSP__ASAN __attribute__((__no_sanitize__("address")))
- #elif __has_attribute(__no_sanitize_address__)
- #define STBSP__ASAN __attribute__((__no_sanitize_address__))
- #elif __has_attribute(__no_address_safety_analysis__)
- #define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
- #endif
- #endif
- #endif
-#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
- #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
- #define STBSP__ASAN __attribute__((__no_sanitize_address__))
- #endif
-#endif
-
-#ifndef STBSP__ASAN
-#define STBSP__ASAN
-#endif
-
-#ifdef STB_SPRINTF_STATIC
-#define STBSP__PUBLICDEC static
-#define STBSP__PUBLICDEF static STBSP__ASAN
-#else
-#ifdef __cplusplus
-#define STBSP__PUBLICDEC extern "C"
-#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
-#else
-#define STBSP__PUBLICDEC extern
-#define STBSP__PUBLICDEF STBSP__ASAN
-#endif
-#endif
-
-#if defined(__has_attribute)
- #if __has_attribute(format)
- #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))
- #endif
-#endif
-
-#ifndef STBSP__ATTRIBUTE_FORMAT
-#define STBSP__ATTRIBUTE_FORMAT(fmt,va)
-#endif
-
-#ifdef _MSC_VER
-#define STBSP__NOTUSED(v) (void)(v)
-#else
-#define STBSP__NOTUSED(v) (void)sizeof(v)
-#endif
-
-#include <stdarg.h> // for va_arg(), va_list()
-#include <stddef.h> // size_t, ptrdiff_t
-
-#ifndef stbsp__uintptr
-#if defined(__riscv) && (__riscv_xlen == 64)
-#define stbsp__uintptr unsigned long long
-#elif defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__)
-#define stbsp__uintptr unsigned long long
-#else
-#define stbsp__uintptr unsigned int
-#endif
-#endif
-
-#ifndef STB_SPRINTF_MIN
-#define STB_SPRINTF_MIN 512 // how many characters per callback
-#endif
-typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);
-
-#ifndef STB_SPRINTF_DECORATE
-#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
-#endif
-
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);
-
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
-STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
-
-#endif // STB_SPRINTF_H_INCLUDE
-
-#ifdef STB_SPRINTF_IMPLEMENTATION
-
-#define stbsp__uint32 unsigned int
-#define stbsp__int32 signed int
-
-#ifdef _MSC_VER
-#define stbsp__uint64 unsigned __int64
-#define stbsp__int64 signed __int64
-#else
-#define stbsp__uint64 unsigned long long
-#define stbsp__int64 signed long long
-#endif
-#define stbsp__uint16 unsigned short
-
-#ifndef stbsp__uintptr
-#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__)
-#define stbsp__uintptr stbsp__uint64
-#else
-#define stbsp__uintptr stbsp__uint32
-#endif
-#endif
-
-#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)
-#if defined(_MSC_VER) && (_MSC_VER < 1900)
-#define STB_SPRINTF_MSVC_MODE
-#endif
-#endif
-
-#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses
-#define STBSP__UNALIGNED(code)
-#else
-#define STBSP__UNALIGNED(code) code
-#endif
-
-#ifndef STB_SPRINTF_NOFLOAT
-// internal float utility functions
-static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits);
-static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value);
-#define STBSP__SPECIAL 0x7000
-#endif
-
-static char stbsp__period = '.';
-static char stbsp__comma = ',';
-static struct
-{
- short temp; // force next field to be 2-byte aligned
- char pair[201];
-} stbsp__digitpair =
-{
- 0,
- "00010203040506070809101112131415161718192021222324"
- "25262728293031323334353637383940414243444546474849"
- "50515253545556575859606162636465666768697071727374"
- "75767778798081828384858687888990919293949596979899"
-};
-
-STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod)
-{
- stbsp__period = pperiod;
- stbsp__comma = pcomma;
-}
-
-#define STBSP__LEFTJUST 1
-#define STBSP__LEADINGPLUS 2
-#define STBSP__LEADINGSPACE 4
-#define STBSP__LEADING_0X 8
-#define STBSP__LEADINGZERO 16
-#define STBSP__INTMAX 32
-#define STBSP__TRIPLET_COMMA 64
-#define STBSP__NEGATIVE 128
-#define STBSP__METRIC_SUFFIX 256
-#define STBSP__HALFWIDTH 512
-#define STBSP__METRIC_NOSPACE 1024
-#define STBSP__METRIC_1024 2048
-#define STBSP__METRIC_JEDEC 4096
-
-static void stbsp__lead_sign(stbsp__uint32 fl, char *sign)
-{
- sign[0] = 0;
- if (fl & STBSP__NEGATIVE) {
- sign[0] = 1;
- sign[1] = '-';
- } else if (fl & STBSP__LEADINGSPACE) {
- sign[0] = 1;
- sign[1] = ' ';
- } else if (fl & STBSP__LEADINGPLUS) {
- sign[0] = 1;
- sign[1] = '+';
- }
-}
-
-static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit)
-{
- char const * sn = s;
-
- // get up to 4-byte alignment
- for (;;) {
- if (((stbsp__uintptr)sn & 3) == 0)
- break;
-
- if (!limit || *sn == 0)
- return (stbsp__uint32)(sn - s);
-
- ++sn;
- --limit;
- }
-
- // scan over 4 bytes at a time to find terminating 0
- // this will intentionally scan up to 3 bytes past the end of buffers,
- // but becase it works 4B aligned, it will never cross page boundaries
- // (hence the STBSP__ASAN markup; the over-read here is intentional
- // and harmless)
- while (limit >= 4) {
- stbsp__uint32 v = *(stbsp__uint32 *)sn;
- // bit hack to find if there's a 0 byte in there
- if ((v - 0x01010101) & (~v) & 0x80808080UL)
- break;
-
- sn += 4;
- limit -= 4;
- }
-
- // handle the last few characters to find actual size
- while (limit && *sn) {
- ++sn;
- --limit;
- }
-
- return (stbsp__uint32)(sn - s);
-}
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)
-{
- static char hex[] = "0123456789abcdefxp";
- static char hexu[] = "0123456789ABCDEFXP";
- char *bf;
- char const *f;
- int tlen = 0;
-
- bf = buf;
- f = fmt;
- for (;;) {
- stbsp__int32 fw, pr, tz;
- stbsp__uint32 fl;
-
- // macros for the callback buffer stuff
- #define stbsp__chk_cb_bufL(bytes) \
- { \
- int len = (int)(bf - buf); \
- if ((len + (bytes)) >= STB_SPRINTF_MIN) { \
- tlen += len; \
- if (0 == (bf = buf = callback(buf, user, len))) \
- goto done; \
- } \
- }
- #define stbsp__chk_cb_buf(bytes) \
- { \
- if (callback) { \
- stbsp__chk_cb_bufL(bytes); \
- } \
- }
- #define stbsp__flush_cb() \
- { \
- stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \
- } // flush if there is even one byte in the buffer
- #define stbsp__cb_buf_clamp(cl, v) \
- cl = v; \
- if (callback) { \
- int lg = STB_SPRINTF_MIN - (int)(bf - buf); \
- if (cl > lg) \
- cl = lg; \
- }
-
- // fast copy everything up to the next % (or end of string)
- for (;;) {
- while (((stbsp__uintptr)f) & 3) {
- schk1:
- if (f[0] == '%')
- goto scandd;
- schk2:
- if (f[0] == 0)
- goto endfmt;
- stbsp__chk_cb_buf(1);
- *bf++ = f[0];
- ++f;
- }
- for (;;) {
- // Check if the next 4 bytes contain %(0x25) or end of string.
- // Using the 'hasless' trick:
- // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
- stbsp__uint32 v, c;
- v = *(stbsp__uint32 *)f;
- c = (~v) & 0x80808080;
- if (((v ^ 0x25252525) - 0x01010101) & c)
- goto schk1;
- if ((v - 0x01010101) & c)
- goto schk2;
- if (callback)
- if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4)
- goto schk1;
- #ifdef STB_SPRINTF_NOUNALIGNED
- if(((stbsp__uintptr)bf) & 3) {
- bf[0] = f[0];
- bf[1] = f[1];
- bf[2] = f[2];
- bf[3] = f[3];
- } else
- #endif
- {
- *(stbsp__uint32 *)bf = v;
- }
- bf += 4;
- f += 4;
- }
- }
- scandd:
-
- ++f;
-
- // ok, we have a percent, read the modifiers first
- fw = 0;
- pr = -1;
- fl = 0;
- tz = 0;
-
- // flags
- for (;;) {
- switch (f[0]) {
- // if we have left justify
- case '-':
- fl |= STBSP__LEFTJUST;
- ++f;
- continue;
- // if we have leading plus
- case '+':
- fl |= STBSP__LEADINGPLUS;
- ++f;
- continue;
- // if we have leading space
- case ' ':
- fl |= STBSP__LEADINGSPACE;
- ++f;
- continue;
- // if we have leading 0x
- case '#':
- fl |= STBSP__LEADING_0X;
- ++f;
- continue;
- // if we have thousand commas
- case '\'':
- fl |= STBSP__TRIPLET_COMMA;
- ++f;
- continue;
- // if we have kilo marker (none->kilo->kibi->jedec)
- case '$':
- if (fl & STBSP__METRIC_SUFFIX) {
- if (fl & STBSP__METRIC_1024) {
- fl |= STBSP__METRIC_JEDEC;
- } else {
- fl |= STBSP__METRIC_1024;
- }
- } else {
- fl |= STBSP__METRIC_SUFFIX;
- }
- ++f;
- continue;
- // if we don't want space between metric suffix and number
- case '_':
- fl |= STBSP__METRIC_NOSPACE;
- ++f;
- continue;
- // if we have leading zero
- case '0':
- fl |= STBSP__LEADINGZERO;
- ++f;
- goto flags_done;
- default: goto flags_done;
- }
- }
- flags_done:
-
- // get the field width
- if (f[0] == '*') {
- fw = va_arg(va, stbsp__uint32);
- ++f;
- } else {
- while ((f[0] >= '0') && (f[0] <= '9')) {
- fw = fw * 10 + f[0] - '0';
- f++;
- }
- }
- // get the precision
- if (f[0] == '.') {
- ++f;
- if (f[0] == '*') {
- pr = va_arg(va, stbsp__uint32);
- ++f;
- } else {
- pr = 0;
- while ((f[0] >= '0') && (f[0] <= '9')) {
- pr = pr * 10 + f[0] - '0';
- f++;
- }
- }
- }
-
- // handle integer size overrides
- switch (f[0]) {
- // are we halfwidth?
- case 'h':
- fl |= STBSP__HALFWIDTH;
- ++f;
- if (f[0] == 'h')
- ++f; // QUARTERWIDTH
- break;
- // are we 64-bit (unix style)
- case 'l':
- fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0);
- ++f;
- if (f[0] == 'l') {
- fl |= STBSP__INTMAX;
- ++f;
- }
- break;
- // are we 64-bit on intmax? (c99)
- case 'j':
- fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0;
- ++f;
- break;
- // are we 64-bit on size_t or ptrdiff_t? (c99)
- case 'z':
- fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;
- ++f;
- break;
- case 't':
- fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;
- ++f;
- break;
- // are we 64-bit (msft style)
- case 'I':
- if ((f[1] == '6') && (f[2] == '4')) {
- fl |= STBSP__INTMAX;
- f += 3;
- } else if ((f[1] == '3') && (f[2] == '2')) {
- f += 3;
- } else {
- fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0);
- ++f;
- }
- break;
- default: break;
- }
-
- // handle each replacement
- switch (f[0]) {
- #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307
- char num[STBSP__NUMSZ];
- char lead[8];
- char tail[8];
- char *s;
- char const *h;
- stbsp__uint32 l, n, cs;
- stbsp__uint64 n64;
-#ifndef STB_SPRINTF_NOFLOAT
- double fv;
-#endif
- stbsp__int32 dp;
- char const *sn;
-
- case 's':
- // get the string
- s = va_arg(va, char *);
- if (s == 0)
- s = (char *)"null";
- // get the length, limited to desired precision
- // always limit to ~0u chars since our counts are 32b
- l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u);
- lead[0] = 0;
- tail[0] = 0;
- pr = 0;
- dp = 0;
- cs = 0;
- // copy the string in
- goto scopy;
-
- case 'c': // char
- // get the character
- s = num + STBSP__NUMSZ - 1;
- *s = (char)va_arg(va, int);
- l = 1;
- lead[0] = 0;
- tail[0] = 0;
- pr = 0;
- dp = 0;
- cs = 0;
- goto scopy;
-
- case 'n': // weird write-bytes specifier
- {
- int *d = va_arg(va, int *);
- *d = tlen + (int)(bf - buf);
- } break;
-
-#ifdef STB_SPRINTF_NOFLOAT
- case 'A': // float
- case 'a': // hex float
- case 'G': // float
- case 'g': // float
- case 'E': // float
- case 'e': // float
- case 'f': // float
- va_arg(va, double); // eat it
- s = (char *)"No float";
- l = 8;
- lead[0] = 0;
- tail[0] = 0;
- pr = 0;
- cs = 0;
- STBSP__NOTUSED(dp);
- goto scopy;
-#else
- case 'A': // hex float
- case 'a': // hex float
- h = (f[0] == 'A') ? hexu : hex;
- fv = va_arg(va, double);
- if (pr == -1)
- pr = 6; // default is 6
- // read the double into a string
- if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv))
- fl |= STBSP__NEGATIVE;
-
- s = num + 64;
-
- stbsp__lead_sign(fl, lead);
-
- if (dp == -1023)
- dp = (n64) ? -1022 : 0;
- else
- n64 |= (((stbsp__uint64)1) << 52);
- n64 <<= (64 - 56);
- if (pr < 15)
- n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4));
-// add leading chars
-
-#ifdef STB_SPRINTF_MSVC_MODE
- *s++ = '0';
- *s++ = 'x';
-#else
- lead[1 + lead[0]] = '0';
- lead[2 + lead[0]] = 'x';
- lead[0] += 2;
-#endif
- *s++ = h[(n64 >> 60) & 15];
- n64 <<= 4;
- if (pr)
- *s++ = stbsp__period;
- sn = s;
-
- // print the bits
- n = pr;
- if (n > 13)
- n = 13;
- if (pr > (stbsp__int32)n)
- tz = pr - n;
- pr = 0;
- while (n--) {
- *s++ = h[(n64 >> 60) & 15];
- n64 <<= 4;
- }
-
- // print the expo
- tail[1] = h[17];
- if (dp < 0) {
- tail[2] = '-';
- dp = -dp;
- } else
- tail[2] = '+';
- n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3));
- tail[0] = (char)n;
- for (;;) {
- tail[n] = '0' + dp % 10;
- if (n <= 3)
- break;
- --n;
- dp /= 10;
- }
-
- dp = (int)(s - sn);
- l = (int)(s - (num + 64));
- s = num + 64;
- cs = 1 + (3 << 24);
- goto scopy;
-
- case 'G': // float
- case 'g': // float
- h = (f[0] == 'G') ? hexu : hex;
- fv = va_arg(va, double);
- if (pr == -1)
- pr = 6;
- else if (pr == 0)
- pr = 1; // default is 6
- // read the double into a string
- if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000))
- fl |= STBSP__NEGATIVE;
-
- // clamp the precision and delete extra zeros after clamp
- n = pr;
- if (l > (stbsp__uint32)pr)
- l = pr;
- while ((l > 1) && (pr) && (sn[l - 1] == '0')) {
- --pr;
- --l;
- }
-
- // should we use %e
- if ((dp <= -4) || (dp > (stbsp__int32)n)) {
- if (pr > (stbsp__int32)l)
- pr = l - 1;
- else if (pr)
- --pr; // when using %e, there is one digit before the decimal
- goto doexpfromg;
- }
- // this is the insane action to get the pr to match %g semantics for %f
- if (dp > 0) {
- pr = (dp < (stbsp__int32)l) ? l - dp : 0;
- } else {
- pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr);
- }
- goto dofloatfromg;
-
- case 'E': // float
- case 'e': // float
- h = (f[0] == 'E') ? hexu : hex;
- fv = va_arg(va, double);
- if (pr == -1)
- pr = 6; // default is 6
- // read the double into a string
- if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000))
- fl |= STBSP__NEGATIVE;
- doexpfromg:
- tail[0] = 0;
- stbsp__lead_sign(fl, lead);
- if (dp == STBSP__SPECIAL) {
- s = (char *)sn;
- cs = 0;
- pr = 0;
- goto scopy;
- }
- s = num + 64;
- // handle leading chars
- *s++ = sn[0];
-
- if (pr)
- *s++ = stbsp__period;
-
- // handle after decimal
- if ((l - 1) > (stbsp__uint32)pr)
- l = pr + 1;
- for (n = 1; n < l; n++)
- *s++ = sn[n];
- // trailing zeros
- tz = pr - (l - 1);
- pr = 0;
- // dump expo
- tail[1] = h[0xe];
- dp -= 1;
- if (dp < 0) {
- tail[2] = '-';
- dp = -dp;
- } else
- tail[2] = '+';
-#ifdef STB_SPRINTF_MSVC_MODE
- n = 5;
-#else
- n = (dp >= 100) ? 5 : 4;
-#endif
- tail[0] = (char)n;
- for (;;) {
- tail[n] = '0' + dp % 10;
- if (n <= 3)
- break;
- --n;
- dp /= 10;
- }
- cs = 1 + (3 << 24); // how many tens
- goto flt_lead;
-
- case 'f': // float
- fv = va_arg(va, double);
- doafloat:
- // do kilos
- if (fl & STBSP__METRIC_SUFFIX) {
- double divisor;
- divisor = 1000.0f;
- if (fl & STBSP__METRIC_1024)
- divisor = 1024.0;
- while (fl < 0x4000000) {
- if ((fv < divisor) && (fv > -divisor))
- break;
- fv /= divisor;
- fl += 0x1000000;
- }
- }
- if (pr == -1)
- pr = 6; // default is 6
- // read the double into a string
- if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr))
- fl |= STBSP__NEGATIVE;
- dofloatfromg:
- tail[0] = 0;
- stbsp__lead_sign(fl, lead);
- if (dp == STBSP__SPECIAL) {
- s = (char *)sn;
- cs = 0;
- pr = 0;
- goto scopy;
- }
- s = num + 64;
-
- // handle the three decimal varieties
- if (dp <= 0) {
- stbsp__int32 i;
- // handle 0.000*000xxxx
- *s++ = '0';
- if (pr)
- *s++ = stbsp__period;
- n = -dp;
- if ((stbsp__int32)n > pr)
- n = pr;
- i = n;
- while (i) {
- if ((((stbsp__uintptr)s) & 3) == 0)
- break;
- *s++ = '0';
- --i;
- }
- while (i >= 4) {
- *(stbsp__uint32 *)s = 0x30303030;
- s += 4;
- i -= 4;
- }
- while (i) {
- *s++ = '0';
- --i;
- }
- if ((stbsp__int32)(l + n) > pr)
- l = pr - n;
- i = l;
- while (i) {
- *s++ = *sn++;
- --i;
- }
- tz = pr - (n + l);
- cs = 1 + (3 << 24); // how many tens did we write (for commas below)
- } else {
- cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0;
- if ((stbsp__uint32)dp >= l) {
- // handle xxxx000*000.0
- n = 0;
- for (;;) {
- if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
- cs = 0;
- *s++ = stbsp__comma;
- } else {
- *s++ = sn[n];
- ++n;
- if (n >= l)
- break;
- }
- }
- if (n < (stbsp__uint32)dp) {
- n = dp - n;
- if ((fl & STBSP__TRIPLET_COMMA) == 0) {
- while (n) {
- if ((((stbsp__uintptr)s) & 3) == 0)
- break;
- *s++ = '0';
- --n;
- }
- while (n >= 4) {
- *(stbsp__uint32 *)s = 0x30303030;
- s += 4;
- n -= 4;
- }
- }
- while (n) {
- if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
- cs = 0;
- *s++ = stbsp__comma;
- } else {
- *s++ = '0';
- --n;
- }
- }
- }
- cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
- if (pr) {
- *s++ = stbsp__period;
- tz = pr;
- }
- } else {
- // handle xxxxx.xxxx000*000
- n = 0;
- for (;;) {
- if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
- cs = 0;
- *s++ = stbsp__comma;
- } else {
- *s++ = sn[n];
- ++n;
- if (n >= (stbsp__uint32)dp)
- break;
- }
- }
- cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
- if (pr)
- *s++ = stbsp__period;
- if ((l - dp) > (stbsp__uint32)pr)
- l = pr + dp;
- while (n < l) {
- *s++ = sn[n];
- ++n;
- }
- tz = pr - (l - dp);
- }
- }
- pr = 0;
-
- // handle k,m,g,t
- if (fl & STBSP__METRIC_SUFFIX) {
- char idx;
- idx = 1;
- if (fl & STBSP__METRIC_NOSPACE)
- idx = 0;
- tail[0] = idx;
- tail[1] = ' ';
- {
- if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'.
- if (fl & STBSP__METRIC_1024)
- tail[idx + 1] = "_KMGT"[fl >> 24];
- else
- tail[idx + 1] = "_kMGT"[fl >> 24];
- idx++;
- // If printing kibits and not in jedec, add the 'i'.
- if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) {
- tail[idx + 1] = 'i';
- idx++;
- }
- tail[0] = idx;
- }
- }
- };
-
- flt_lead:
- // get the length that we copied
- l = (stbsp__uint32)(s - (num + 64));
- s = num + 64;
- goto scopy;
-#endif
-
- case 'B': // upper binary
- case 'b': // lower binary
- h = (f[0] == 'B') ? hexu : hex;
- lead[0] = 0;
- if (fl & STBSP__LEADING_0X) {
- lead[0] = 2;
- lead[1] = '0';
- lead[2] = h[0xb];
- }
- l = (8 << 4) | (1 << 8);
- goto radixnum;
-
- case 'o': // octal
- h = hexu;
- lead[0] = 0;
- if (fl & STBSP__LEADING_0X) {
- lead[0] = 1;
- lead[1] = '0';
- }
- l = (3 << 4) | (3 << 8);
- goto radixnum;
-
- case 'p': // pointer
- fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0;
- pr = sizeof(void *) * 2;
- fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros
- // fall through - to X
-
- case 'X': // upper hex
- case 'x': // lower hex
- h = (f[0] == 'X') ? hexu : hex;
- l = (4 << 4) | (4 << 8);
- lead[0] = 0;
- if (fl & STBSP__LEADING_0X) {
- lead[0] = 2;
- lead[1] = '0';
- lead[2] = h[16];
- }
- radixnum:
- // get the number
- if (fl & STBSP__INTMAX)
- n64 = va_arg(va, stbsp__uint64);
- else
- n64 = va_arg(va, stbsp__uint32);
-
- s = num + STBSP__NUMSZ;
- dp = 0;
- // clear tail, and clear leading if value is zero
- tail[0] = 0;
- if (n64 == 0) {
- lead[0] = 0;
- if (pr == 0) {
- l = 0;
- cs = 0;
- goto scopy;
- }
- }
- // convert to string
- for (;;) {
- *--s = h[n64 & ((1 << (l >> 8)) - 1)];
- n64 >>= (l >> 8);
- if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr)))
- break;
- if (fl & STBSP__TRIPLET_COMMA) {
- ++l;
- if ((l & 15) == ((l >> 4) & 15)) {
- l &= ~15;
- *--s = stbsp__comma;
- }
- }
- };
- // get the tens and the comma pos
- cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24);
- // get the length that we copied
- l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
- // copy it
- goto scopy;
-
- case 'u': // unsigned
- case 'i':
- case 'd': // integer
- // get the integer and abs it
- if (fl & STBSP__INTMAX) {
- stbsp__int64 i64 = va_arg(va, stbsp__int64);
- n64 = (stbsp__uint64)i64;
- if ((f[0] != 'u') && (i64 < 0)) {
- n64 = (stbsp__uint64)-i64;
- fl |= STBSP__NEGATIVE;
- }
- } else {
- stbsp__int32 i = va_arg(va, stbsp__int32);
- n64 = (stbsp__uint32)i;
- if ((f[0] != 'u') && (i < 0)) {
- n64 = (stbsp__uint32)-i;
- fl |= STBSP__NEGATIVE;
- }
- }
-
-#ifndef STB_SPRINTF_NOFLOAT
- if (fl & STBSP__METRIC_SUFFIX) {
- if (n64 < 1024)
- pr = 0;
- else if (pr == -1)
- pr = 1;
- fv = (double)(stbsp__int64)n64;
- goto doafloat;
- }
-#endif
-
- // convert to string
- s = num + STBSP__NUMSZ;
- l = 0;
-
- for (;;) {
- // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
- char *o = s - 8;
- if (n64 >= 100000000) {
- n = (stbsp__uint32)(n64 % 100000000);
- n64 /= 100000000;
- } else {
- n = (stbsp__uint32)n64;
- n64 = 0;
- }
- if ((fl & STBSP__TRIPLET_COMMA) == 0) {
- do {
- s -= 2;
- *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
- n /= 100;
- } while (n);
- }
- while (n) {
- if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
- l = 0;
- *--s = stbsp__comma;
- --o;
- } else {
- *--s = (char)(n % 10) + '0';
- n /= 10;
- }
- }
- if (n64 == 0) {
- if ((s[0] == '0') && (s != (num + STBSP__NUMSZ)))
- ++s;
- break;
- }
- while (s != o)
- if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
- l = 0;
- *--s = stbsp__comma;
- --o;
- } else {
- *--s = '0';
- }
- }
-
- tail[0] = 0;
- stbsp__lead_sign(fl, lead);
-
- // get the length that we copied
- l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
- if (l == 0) {
- *--s = '0';
- l = 1;
- }
- cs = l + (3 << 24);
- if (pr < 0)
- pr = 0;
-
- scopy:
- // get fw=leading/trailing space, pr=leading zeros
- if (pr < (stbsp__int32)l)
- pr = l;
- n = pr + lead[0] + tail[0] + tz;
- if (fw < (stbsp__int32)n)
- fw = n;
- fw -= n;
- pr -= l;
-
- // handle right justify and leading zeros
- if ((fl & STBSP__LEFTJUST) == 0) {
- if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr
- {
- pr = (fw > pr) ? fw : pr;
- fw = 0;
- } else {
- fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas
- }
- }
-
- // copy the spaces and/or zeros
- if (fw + pr) {
- stbsp__int32 i;
- stbsp__uint32 c;
-
- // copy leading spaces (or when doing %8.4d stuff)
- if ((fl & STBSP__LEFTJUST) == 0)
- while (fw > 0) {
- stbsp__cb_buf_clamp(i, fw);
- fw -= i;
- while (i) {
- if ((((stbsp__uintptr)bf) & 3) == 0)
- break;
- *bf++ = ' ';
- --i;
- }
- while (i >= 4) {
- *(stbsp__uint32 *)bf = 0x20202020;
- bf += 4;
- i -= 4;
- }
- while (i) {
- *bf++ = ' ';
- --i;
- }
- stbsp__chk_cb_buf(1);
- }
-
- // copy leader
- sn = lead + 1;
- while (lead[0]) {
- stbsp__cb_buf_clamp(i, lead[0]);
- lead[0] -= (char)i;
- while (i) {
- *bf++ = *sn++;
- --i;
- }
- stbsp__chk_cb_buf(1);
- }
-
- // copy leading zeros
- c = cs >> 24;
- cs &= 0xffffff;
- cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0;
- while (pr > 0) {
- stbsp__cb_buf_clamp(i, pr);
- pr -= i;
- if ((fl & STBSP__TRIPLET_COMMA) == 0) {
- while (i) {
- if ((((stbsp__uintptr)bf) & 3) == 0)
- break;
- *bf++ = '0';
- --i;
- }
- while (i >= 4) {
- *(stbsp__uint32 *)bf = 0x30303030;
- bf += 4;
- i -= 4;
- }
- }
- while (i) {
- if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) {
- cs = 0;
- *bf++ = stbsp__comma;
- } else
- *bf++ = '0';
- --i;
- }
- stbsp__chk_cb_buf(1);
- }
- }
-
- // copy leader if there is still one
- sn = lead + 1;
- while (lead[0]) {
- stbsp__int32 i;
- stbsp__cb_buf_clamp(i, lead[0]);
- lead[0] -= (char)i;
- while (i) {
- *bf++ = *sn++;
- --i;
- }
- stbsp__chk_cb_buf(1);
- }
-
- // copy the string
- n = l;
- while (n) {
- stbsp__int32 i;
- stbsp__cb_buf_clamp(i, n);
- n -= i;
- STBSP__UNALIGNED(while (i >= 4) {
- *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s;
- bf += 4;
- s += 4;
- i -= 4;
- })
- while (i) {
- *bf++ = *s++;
- --i;
- }
- stbsp__chk_cb_buf(1);
- }
-
- // copy trailing zeros
- while (tz) {
- stbsp__int32 i;
- stbsp__cb_buf_clamp(i, tz);
- tz -= i;
- while (i) {
- if ((((stbsp__uintptr)bf) & 3) == 0)
- break;
- *bf++ = '0';
- --i;
- }
- while (i >= 4) {
- *(stbsp__uint32 *)bf = 0x30303030;
- bf += 4;
- i -= 4;
- }
- while (i) {
- *bf++ = '0';
- --i;
- }
- stbsp__chk_cb_buf(1);
- }
-
- // copy tail if there is one
- sn = tail + 1;
- while (tail[0]) {
- stbsp__int32 i;
- stbsp__cb_buf_clamp(i, tail[0]);
- tail[0] -= (char)i;
- while (i) {
- *bf++ = *sn++;
- --i;
- }
- stbsp__chk_cb_buf(1);
- }
-
- // handle the left justify
- if (fl & STBSP__LEFTJUST)
- if (fw > 0) {
- while (fw) {
- stbsp__int32 i;
- stbsp__cb_buf_clamp(i, fw);
- fw -= i;
- while (i) {
- if ((((stbsp__uintptr)bf) & 3) == 0)
- break;
- *bf++ = ' ';
- --i;
- }
- while (i >= 4) {
- *(stbsp__uint32 *)bf = 0x20202020;
- bf += 4;
- i -= 4;
- }
- while (i--)
- *bf++ = ' ';
- stbsp__chk_cb_buf(1);
- }
- }
- break;
-
- default: // unknown, just copy code
- s = num + STBSP__NUMSZ - 1;
- *s = f[0];
- l = 1;
- fw = fl = 0;
- lead[0] = 0;
- tail[0] = 0;
- pr = 0;
- dp = 0;
- cs = 0;
- goto scopy;
- }
- ++f;
- }
-endfmt:
-
- if (!callback)
- *bf = 0;
- else
- stbsp__flush_cb();
-
-done:
- return tlen + (int)(bf - buf);
-}
-
-// cleanup
-#undef STBSP__LEFTJUST
-#undef STBSP__LEADINGPLUS
-#undef STBSP__LEADINGSPACE
-#undef STBSP__LEADING_0X
-#undef STBSP__LEADINGZERO
-#undef STBSP__INTMAX
-#undef STBSP__TRIPLET_COMMA
-#undef STBSP__NEGATIVE
-#undef STBSP__METRIC_SUFFIX
-#undef STBSP__NUMSZ
-#undef stbsp__chk_cb_bufL
-#undef stbsp__chk_cb_buf
-#undef stbsp__flush_cb
-#undef stbsp__cb_buf_clamp
-
-// ============================================================================
-// wrapper functions
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)
-{
- int result;
- va_list va;
- va_start(va, fmt);
- result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
- va_end(va);
- return result;
-}
-
-typedef struct stbsp__context {
- char *buf;
- int count;
- int length;
- char tmp[STB_SPRINTF_MIN];
-} stbsp__context;
-
-static char *stbsp__clamp_callback(const char *buf, void *user, int len)
-{
- stbsp__context *c = (stbsp__context *)user;
- c->length += len;
-
- if (len > c->count)
- len = c->count;
-
- if (len) {
- if (buf != c->buf) {
- const char *s, *se;
- char *d;
- d = c->buf;
- s = buf;
- se = buf + len;
- do {
- *d++ = *s++;
- } while (s < se);
- }
- c->buf += len;
- c->count -= len;
- }
-
- if (c->count <= 0)
- return c->tmp;
- return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can
-}
-
-static char * stbsp__count_clamp_callback( const char * buf, void * user, int len )
-{
- stbsp__context * c = (stbsp__context*)user;
- (void) sizeof(buf);
-
- c->length += len;
- return c->tmp; // go direct into buffer if you can
-}
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
-{
- stbsp__context c;
-
- if ( (count == 0) && !buf )
- {
- c.length = 0;
-
- STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va );
- }
- else
- {
- int l;
-
- c.buf = buf;
- c.count = count;
- c.length = 0;
-
- STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );
-
- // zero-terminate
- l = (int)( c.buf - buf );
- if ( l >= count ) // should never be greater, only equal (or less) than count
- l = count - 1;
- buf[l] = 0;
- }
-
- return c.length;
-}
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...)
-{
- int result;
- va_list va;
- va_start(va, fmt);
-
- result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va);
- va_end(va);
-
- return result;
-}
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va)
-{
- return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
-}
-
-// =======================================================================
-// low level float utility functions
-
-#ifndef STB_SPRINTF_NOFLOAT
-
-// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
-#define STBSP__COPYFP(dest, src) \
- { \
- int cn; \
- for (cn = 0; cn < 8; cn++) \
- ((char *)&dest)[cn] = ((char *)&src)[cn]; \
- }
-
-// get float info
-static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value)
-{
- double d;
- stbsp__int64 b = 0;
-
- // load value and round at the frac_digits
- d = value;
-
- STBSP__COPYFP(b, d);
-
- *bits = b & ((((stbsp__uint64)1) << 52) - 1);
- *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023);
-
- return (stbsp__int32)((stbsp__uint64) b >> 63);
-}
-
-static double const stbsp__bot[23] = {
- 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011,
- 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022
-};
-static double const stbsp__negbot[22] = {
- 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011,
- 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022
-};
-static double const stbsp__negboterr[22] = {
- -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023,
- 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029,
- -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035,
- 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039
-};
-static double const stbsp__top[13] = {
- 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299
-};
-static double const stbsp__negtop[13] = {
- 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299
-};
-static double const stbsp__toperr[13] = {
- 8388608,
- 6.8601809640529717e+028,
- -7.253143638152921e+052,
- -4.3377296974619174e+075,
- -1.5559416129466825e+098,
- -3.2841562489204913e+121,
- -3.7745893248228135e+144,
- -1.7356668416969134e+167,
- -3.8893577551088374e+190,
- -9.9566444326005119e+213,
- 6.3641293062232429e+236,
- -5.2069140800249813e+259,
- -5.2504760255204387e+282
-};
-static double const stbsp__negtoperr[13] = {
- 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109,
- -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201,
- 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293,
- 8.0970921678014997e-317
-};
-
-#if defined(_MSC_VER) && (_MSC_VER <= 1200)
-static stbsp__uint64 const stbsp__powten[20] = {
- 1,
- 10,
- 100,
- 1000,
- 10000,
- 100000,
- 1000000,
- 10000000,
- 100000000,
- 1000000000,
- 10000000000,
- 100000000000,
- 1000000000000,
- 10000000000000,
- 100000000000000,
- 1000000000000000,
- 10000000000000000,
- 100000000000000000,
- 1000000000000000000,
- 10000000000000000000U
-};
-#define stbsp__tento19th ((stbsp__uint64)1000000000000000000)
-#else
-static stbsp__uint64 const stbsp__powten[20] = {
- 1,
- 10,
- 100,
- 1000,
- 10000,
- 100000,
- 1000000,
- 10000000,
- 100000000,
- 1000000000,
- 10000000000ULL,
- 100000000000ULL,
- 1000000000000ULL,
- 10000000000000ULL,
- 100000000000000ULL,
- 1000000000000000ULL,
- 10000000000000000ULL,
- 100000000000000000ULL,
- 1000000000000000000ULL,
- 10000000000000000000ULL
-};
-#define stbsp__tento19th (1000000000000000000ULL)
-#endif
-
-#define stbsp__ddmulthi(oh, ol, xh, yh) \
- { \
- double ahi = 0, alo, bhi = 0, blo; \
- stbsp__int64 bt; \
- oh = xh * yh; \
- STBSP__COPYFP(bt, xh); \
- bt &= ((~(stbsp__uint64)0) << 27); \
- STBSP__COPYFP(ahi, bt); \
- alo = xh - ahi; \
- STBSP__COPYFP(bt, yh); \
- bt &= ((~(stbsp__uint64)0) << 27); \
- STBSP__COPYFP(bhi, bt); \
- blo = yh - bhi; \
- ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \
- }
-
-#define stbsp__ddtoS64(ob, xh, xl) \
- { \
- double ahi = 0, alo, vh, t; \
- ob = (stbsp__int64)xh; \
- vh = (double)ob; \
- ahi = (xh - vh); \
- t = (ahi - xh); \
- alo = (xh - (ahi - t)) - (vh + t); \
- ob += (stbsp__int64)(ahi + alo + xl); \
- }
-
-#define stbsp__ddrenorm(oh, ol) \
- { \
- double s; \
- s = oh + ol; \
- ol = ol - (s - oh); \
- oh = s; \
- }
-
-#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);
-
-#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);
-
-static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350
-{
- double ph, pl;
- if ((power >= 0) && (power <= 22)) {
- stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]);
- } else {
- stbsp__int32 e, et, eb;
- double p2h, p2l;
-
- e = power;
- if (power < 0)
- e = -e;
- et = (e * 0x2c9) >> 14; /* %23 */
- if (et > 13)
- et = 13;
- eb = e - (et * 23);
-
- ph = d;
- pl = 0.0;
- if (power < 0) {
- if (eb) {
- --eb;
- stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]);
- stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]);
- }
- if (et) {
- stbsp__ddrenorm(ph, pl);
- --et;
- stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]);
- stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]);
- ph = p2h;
- pl = p2l;
- }
- } else {
- if (eb) {
- e = eb;
- if (eb > 22)
- eb = 22;
- e -= eb;
- stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]);
- if (e) {
- stbsp__ddrenorm(ph, pl);
- stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]);
- stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl);
- ph = p2h;
- pl = p2l;
- }
- }
- if (et) {
- stbsp__ddrenorm(ph, pl);
- --et;
- stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]);
- stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]);
- ph = p2h;
- pl = p2l;
- }
- }
- }
- stbsp__ddrenorm(ph, pl);
- *ohi = ph;
- *olo = pl;
-}
-
-// given a float value, returns the significant bits in bits, and the position of the
-// decimal point in decimal_pos. +/-INF and NAN are specified by special values
-// returned in the decimal_pos parameter.
-// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
-static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits)
-{
- double d;
- stbsp__int64 bits = 0;
- stbsp__int32 expo, e, ng, tens;
-
- d = value;
- STBSP__COPYFP(bits, d);
- expo = (stbsp__int32)((bits >> 52) & 2047);
- ng = (stbsp__int32)((stbsp__uint64) bits >> 63);
- if (ng)
- d = -d;
-
- if (expo == 2047) // is nan or inf?
- {
- *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf";
- *decimal_pos = STBSP__SPECIAL;
- *len = 3;
- return ng;
- }
-
- if (expo == 0) // is zero or denormal
- {
- if (((stbsp__uint64) bits << 1) == 0) // do zero
- {
- *decimal_pos = 1;
- *start = out;
- out[0] = '0';
- *len = 1;
- return ng;
- }
- // find the right expo for denormals
- {
- stbsp__int64 v = ((stbsp__uint64)1) << 51;
- while ((bits & v) == 0) {
- --expo;
- v >>= 1;
- }
- }
- }
-
- // find the decimal exponent as well as the decimal bits of the value
- {
- double ph, pl;
-
- // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
- tens = expo - 1023;
- tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1);
-
- // move the significant bits into position and stick them into an int
- stbsp__raise_to_power10(&ph, &pl, d, 18 - tens);
-
- // get full as much precision from double-double as possible
- stbsp__ddtoS64(bits, ph, pl);
-
- // check if we undershot
- if (((stbsp__uint64)bits) >= stbsp__tento19th)
- ++tens;
- }
-
- // now do the rounding in integer land
- frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits);
- if ((frac_digits < 24)) {
- stbsp__uint32 dg = 1;
- if ((stbsp__uint64)bits >= stbsp__powten[9])
- dg = 10;
- while ((stbsp__uint64)bits >= stbsp__powten[dg]) {
- ++dg;
- if (dg == 20)
- goto noround;
- }
- if (frac_digits < dg) {
- stbsp__uint64 r;
- // add 0.5 at the right position and round
- e = dg - frac_digits;
- if ((stbsp__uint32)e >= 24)
- goto noround;
- r = stbsp__powten[e];
- bits = bits + (r / 2);
- if ((stbsp__uint64)bits >= stbsp__powten[dg])
- ++tens;
- bits /= r;
- }
- noround:;
- }
-
- // kill long trailing runs of zeros
- if (bits) {
- stbsp__uint32 n;
- for (;;) {
- if (bits <= 0xffffffff)
- break;
- if (bits % 1000)
- goto donez;
- bits /= 1000;
- }
- n = (stbsp__uint32)bits;
- while ((n % 1000) == 0)
- n /= 1000;
- bits = n;
- donez:;
- }
-
- // convert to string
- out += 64;
- e = 0;
- for (;;) {
- stbsp__uint32 n;
- char *o = out - 8;
- // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
- if (bits >= 100000000) {
- n = (stbsp__uint32)(bits % 100000000);
- bits /= 100000000;
- } else {
- n = (stbsp__uint32)bits;
- bits = 0;
- }
- while (n) {
- out -= 2;
- *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
- n /= 100;
- e += 2;
- }
- if (bits == 0) {
- if ((e) && (out[0] == '0')) {
- ++out;
- --e;
- }
- break;
- }
- while (out != o) {
- *--out = '0';
- ++e;
- }
- }
-
- *decimal_pos = tens;
- *start = out;
- *len = e;
- return ng;
-}
-
-#undef stbsp__ddmulthi
-#undef stbsp__ddrenorm
-#undef stbsp__ddmultlo
-#undef stbsp__ddmultlos
-#undef STBSP__SPECIAL
-#undef STBSP__COPYFP
-
-#endif // STB_SPRINTF_NOFLOAT
-
-// clean up
-#undef stbsp__uint16
-#undef stbsp__uint32
-#undef stbsp__int32
-#undef stbsp__uint64
-#undef stbsp__int64
-#undef STBSP__UNALIGNED
-
-#endif // STB_SPRINTF_IMPLEMENTATION
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2017 Sean Barrett
-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.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-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 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.
-------------------------------------------------------------------------------
-*/
diff --git a/include/ulib/stdio.h b/include/ulib/stdio.h
deleted file mode 100644
index a6df21d..0000000
--- a/include/ulib/stdio.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-/* Standard I/O Library for Lume OS */
-
-#ifndef _STDIO_H
-#define _STDIO_H
-
-#include <stdarg.h>
-#include "ulib/types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*Type Definition (Adjusted According to Architecture)*/
-#ifndef NULL
-#define NULL ((void *)0)
-#endif
-
-/*Macro definition*/
-#define EOF (-1)
-
-#define SEEK_SET 0
-#define SEEK_CUR 1
-#define SEEK_END 2
-
-#define BUFSIZ 1024
-
-
-/* FILE structure */
-typedef struct
-{
- int fd;
- int flags;
- int error;
- int eof;
-
- char buffer[BUFSIZ];
-
- int buf_write_pos;
-
- int buf_read_pos;
- int buf_read_cnt;
-
- int buf_mode;
-} FILE;
-
-/* standard stream object */
-extern FILE *stdin;
-extern FILE *stdout;
-extern FILE *stderr;
-
-/* Function Declaration */
-
-/* File Operations */
-FILE *fopen(const char *filename, const char *mode);
-int fclose(FILE *stream);
-int fflush(FILE *stream); // Not implement
-
-/* Formatted input and output */
-int printf(const char *format, ...);
-int scanf(const char *format, ...);
-int sprintf(char *str, const char *format, ...);
-int snprintf(char *str, uint64 size, const char *format, ...);
-int vsnprintf(char *str, uint64 size, const char *format, va_list ap);
-int sscanf(const char *str, const char *format, ...);
-
-int fprintf(FILE *stream, const char *format, ...);
-int fscanf(FILE *stream, const char *format, ...);
-
-/* Character/String Read and Write */
-int fputc(int c, FILE *stream);
-int fputs(const char *s, FILE *stream);
-int fgetc(FILE *stream);
-char *fgets(char *str, int n, FILE *stream);
-int ungetc(int c, FILE *stream);
-
-/* Common Macros/Wrapper Functions */
-#define getchar() fgetc(stdin)
-#define putchar(c) fputc((c), stdout)
-
-/* Binary read and write */
-uint64 fread(void *ptr, uint64 size, uint64 nmemb, FILE *stream);
-uint64 fwrite(const void *ptr, uint64 size, uint64 nmemb, FILE *stream);
-
-/* File location */
-int fseek(FILE *stream, long offset, int whence);
-long ftell(FILE *stream);
-void rewind(FILE *stream);
-
-/* Status Inquiry */
-int feof(FILE *stream);
-int ferror(FILE *stream);
-void clearerr(FILE *stream);
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* _STDIO_H */ \ No newline at end of file
diff --git a/include/ulib/stdlib.h b/include/ulib/stdlib.h
deleted file mode 100644
index 6d8f75d..0000000
--- a/include/ulib/stdlib.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-
-#ifndef _STDLIB_H
-#define _STDLIB_H
-
-#include "ulib/types.h"
-#include "ulib/limits.h"
-#include "ulib/errno.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#ifndef NULL
-#define NULL ((void *)0)
-#endif
-
-#define ERANGE 34 /* Math result not representable */
-#define ENOMEM 12 /* Out of memory */
-
-#define EXIT_SUCCESS 0
-#define EXIT_FAILURE 1
-#define RAND_MAX 32767
-#define MB_CUR_MAX 1
-
-typedef struct
-{
- int quot;
- int rem;
-} div_t;
-typedef struct
-{
- long quot;
- long rem;
-} ldiv_t;
-typedef struct
-{
- long long quot;
- long long rem;
-} lldiv_t;
-
-typedef int (*__compar_fn_t)(const void *, const void *);
-
-/* Memory Management */
-void *malloc(size_t size);
-void free(void *ptr);
-void *calloc(size_t nmemb, size_t size);
-void *realloc(void *ptr, size_t size);
-
-/* Process Control */
-int exit(int status);
-void abort(void);
-int atexit(void (*function)(void));
-char *getenv(const char *name);
-int system(const char *command);
-
-/* String Conversion */
-int atoi(const char *nptr);
-long atol(const char *nptr);
-long long atoll(const char *nptr);
-double atof(const char *nptr);
-
-long strtol(const char *nptr, char **endptr, int base);
-unsigned long strtoul(const char *nptr, char **endptr, int base);
-long long strtoll(const char *nptr, char **endptr, int base);
-unsigned long long strtoull(const char *nptr, char **endptr, int base);
-
-double strtod(const char *nptr, char **endptr);
-float strtof(const char *nptr, char **endptr);
-
-/* Search & Sort*/
-void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
-void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
-
-/* Math & Mics */
-int abs(int j);
-long labs(long j);
-long long llabs(long long j);
-div_t div(int numer, int denom);
-ldiv_t ldiv(long numer, long denom);
-lldiv_t lldiv(long long numer, long long denom);
-
-int rand(void);
-void srand(unsigned int seed);
-
-int mblen(const char *s, size_t n);
-int mbtowc(wchar_t *pwc, const char *s, size_t n);
-int wctomb(char *s, wchar_t wchar);
-size_t mbstowcs(wchar_t *dest, const char *src, size_t n);
-size_t wcstombs(char *dest, const wchar_t *src, size_t n);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _STDLIB_H */ \ No newline at end of file
diff --git a/include/ulib/string.h b/include/ulib/string.h
deleted file mode 100644
index c7519ea..0000000
--- a/include/ulib/string.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-#ifndef _STRING_H
-#define _STRING_H
-
-#include "ulib/types.h"
-#include "ulib/errno.h"
-
-#ifndef _SIZE_T
-#define _SIZE_T
-typedef unsigned long long size_t;
-#endif
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /* Memory Manipulation*/
- void *memcpy(void *dest, const void *src, size_t n);
-
- void *memmove(void *dest, const void *src, size_t n);
-
- void *memset(void *dest, int c, size_t n);
-
- int memcmp(const void *s1, const void *s2, size_t n);
-
- void *memchr(const void *s, int c, size_t n);
-
- /* String Manipulation */
-
- size_t strlen(const char *s);
-
- char *strcpy(char *dest, const char *src);
-
- char *strncpy(char *dest, const char *src, size_t n);
-
- char *strcat(char *dest, const char *src);
-
- char *strncat(char *dest, const char *src, size_t n);
-
- int strcmp(const char *s1, const char *s2);
-
- int strncmp(const char *s1, const char *s2, size_t n);
-
- char *strchr(const char *s, int c);
-
- char *strrchr(const char *s, int c);
-
- char *strstr(const char *haystack, const char *needle);
-
- char *strdup(const char *s);
-
- size_t strlcpy(char *dest, const char *src, size_t size);
-
- size_t strlcat(char *dest, const char *src, size_t size);
-
- size_t strspn(const char *s, const char *accept);
-
- size_t strcspn(const char *s, const char *reject);
-
- char *strpbrk(const char *s, const char *accept);
-
- char *strtok(char *str, const char *delim);
-
- char *strerror(int errnum);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _STRING_H */ \ No newline at end of file
diff --git a/include/ulib/types.h b/include/ulib/types.h
deleted file mode 100644
index 69ee233..0000000
--- a/include/ulib/types.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard QIn
- */
-#ifndef _ULIB_TYPES_H
-#define _ULIB_TYPES_H
-
-#include "common/types.h"
-
-#ifndef _ULIB_ALIASES
-#define _ULIB_ALIASES
-typedef unsigned int uint;
-typedef unsigned short ushort;
-typedef unsigned char uchar;
-#endif
-
-#ifndef NULL
-#define NULL ((void *)0)
-#endif
-
-#ifndef __cplusplus
-typedef int wchar_t;
-#endif
-
-#endif \ No newline at end of file
diff --git a/include/user/user.h b/include/user/user.h
deleted file mode 100644
index 7af8bdd..0000000
--- a/include/user/user.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2026 Richard Qin
- */
-#pragma once
-
-typedef unsigned long long uint64;
-
-#define AT_FDCWD -100
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- // Raw Linux System Calls
- int sys_openat(int dfd, const char *filename, int flags, int mode);
- int sys_mknodat(int dfd, const char *filename, int mode, int dev);
- int sys_mkdirat(int dfd, const char *path, int mode);
- int sys_unlinkat(int dfd, const char *path, int flags);
- int sys_linkat(int olddfd, const char *oldpath, int newdfd, const char *newpath, int flags);
- int sys_chdir(const char *path);
- int sys_dup(int oldfd);
- int sys_fstat(int fd, void *statbuf);
- int sys_read(int fd, void *buf, int count);
- int sys_write(int fd, const void *buf, int count);
- int sys_close(int fd);
- int sys_pipe2(int pipefd[2], int flags);
- int sys_clone(unsigned long flags, void *stack, int *ptid, void *tls, int *ctid);
- int sys_execve(const char *path, char *const argv[], char *const envp[]);
- int sys_wait4(int pid, int *status, int options, void *rusage);
- int sys_exit(int status);
- int sys_getpid();
- int sys_brk(int n);
- int sys_kill(int pid, int sig);
- int sys_nanosleep(void *req, void *rem);
- int sys_reboot(int magic1, int magic2, int cmd, void *arg);
- int sys_lseek(int fd, int offset, int whence);
-
- // Custom
- void sys_putc(char c);
- int sys_disk_test();
-
- // POSIX Wrappers
- int fork();
- int exit(int status);
- int wait(uint64 status_addr);
- int pipe(int p[]);
- int write(int fd, const void *buf, int n);
- int read(int fd, void *buf, int n);
- int close(int fd);
- int kill(int pid);
- int exec(char *, char **);
- int open(const char *, int);
- int mknod(const char *, short, short);
- int unlink(const char *);
- int link(const char *, const char *);
- int fstat(int fd, void *st);
- int mkdir(const char *);
- int chdir(const char *);
- int dup(int fd);
- int getpid();
- char *sbrk(int n);
- int sleep(int ticks);
- int uptime();
- int shutdown();
- int lseek(int fd, int offset, int whence);
-
- // Debug
- void putc(char c);
- int disk_test();
-
-#ifdef __cplusplus
-}
-#endif \ No newline at end of file