============================
This document provides the internal API reference for Serix kernel subsystems. It specifies the contracts between kernel modules including memory management, task scheduling, interrupt handling, hardware abstraction, and system calls.
The Serix kernel is a microkernel-style operating system written in Rust, featuring capability-based security and a workspace-based cargo architecture.
Type Safety Leverage Rust's type system for compile-time safety guarantees
Minimal Unsafe Isolate unsafe operations to specific hardware abstraction modules
Zero-Cost Abstractions Ensure abstractions compile to optimal machine code with no runtime overhead
Clear Ownership Explicit ownership and borrowing semantics throughout the codebase
Error Handling Use Result and Option types where appropriate; panic on unrecoverable errors
The kernel consists of independent workspace crates
kernel/ Entry point, syscalls, global initialization
memory/ Page tables, heap allocator, frame allocation
hal/ Hardware abstraction (serial, CPU topology)
apic/ APIC interrupt controller (Local APIC, I/O APIC, timer)
idt/ Interrupt Descriptor Table management
graphics/ Framebuffer console and drawing primitives
task/ Async executor, scheduler, task control blocks
capability/ Capability-based security system
drivers/ Device drivers (VirtIO, PCI, console)
vfs/ Virtual filesystem (ramdisk, INode abstraction)
fs/ FAT32 filesystem driver
ipc/ Inter-process communication
loader/ ELF userspace binary loader
ulib/ Userspace library (syscall wrappers)
System Calls 10 syscalls implemented: serix_write, serix_read, serix_open, serix_close, serix_seek, serix_exit, serix_yield, serix_send, serix_recv, serix_recv_block
VFS Path resolution via lookup_path(), global VFS root, FAT32 filesystem mounted from VirtIO block device
Task Scheduler Preemptive round-robin scheduling with LAPIC timer at ~625 Hz
Filesystem FAT32 driver with BPB parsing, cluster chains, directory entries (8.3+LFN), file read/write
File Descriptors Global FD table keyed by (task_id, fd); open/close/seek operations
Userspace Init binary loads and executes from ramdisk
Memory Management Working page tables, heap allocator, frame allocation
Interrupts IDT loaded, APIC enabled, timer and keyboard interrupts functional
The memory subsystem provides page table management, heap allocation, and physical frame allocation. All physical RAM is mapped at virtual offset 0xFFFF_8000_0000_0000 (HHDM - Higher Half Direct Map).
init_offset_page_table()
Initialize offset page table mapper with active page table
pub unsafe fn init_offset_page_table(offset: VirtAddr) -> OffsetPageTable<'static>
Parameters:
offset
Virtual address offset for physical memory mapping, typically
0xFFFF_8000_0000_0000 from Limine HHDM response
Returns:
OffsetPageTable<'static> mapper instance
Safety:
- Caller must ensure offset correctly maps physical memory
- Must be called only once during boot
- Requires valid CR3 register value
Example usage
let phys_mem_offset = VirtAddr::new(0xFFFF_8000_0000_0000); let mut mapper = unsafe { memory::init_offset_page_table(phys_mem_offset) };
// Translate virtual to physical use x86_64::structures::paging::Page; let page = Page::containing_address(VirtAddr::new(0x4444_4444_0000)); mapper.translate_page(page);
Notes:
Returns reference to active level-4 page table. Enables direct physical
memory access via offset mapping. All physical addresses converted as:
virt = 0xFFFF_8000_0000_0000 + phys
BootFrameAllocator
Physical frame allocator using bootloader memory map
pub struct BootFrameAllocator {
}
impl BootFrameAllocator {
}
Purpose: Allocates physical memory frames (4KiB pages) from usable memory regions identified by the bootloader
Trait Implementation
unsafe impl FrameAllocator<Size4KiB> for BootFrameAllocator {
}
Algorithm: Simple bump allocator, no deallocation support
Example usage
let mmap_response = MMAP_REQ.get_response().expect("No memory map");
let entries = mmap_response.entries();
let mut frame_allocator = BootFrameAllocator::new(entries);
// Allocate a frame
if let Some(frame) = frame_allocator.allocate_frame() {
}
StaticBootFrameAllocator
Pre-heap frame allocator using static array storage
pub struct StaticBootFrameAllocator { }
impl StaticBootFrameAllocator { }
Purpose:
Frame allocator that works before heap initialization, stores frames
in static array
Storage
pub static mut BOOT_FRAMES: [Option; MAX_BOOT_FRAMES] = pub const MAX_BOOT_FRAMES: usize = 65536;
Example usage
// Preallocate frames to static array let mut frame_count = 0; for region in usable_regions { }
// Create allocator let mut frame_alloc = StaticBootFrameAllocator::new(frame_count); memory::init_heap(&mut mapper, &mut frame_alloc);
## Heap Management
init_heap()
Map and initialize kernel heap for dynamic memory allocation
pub fn init_heap(
)
Parameters: mapper Page table mapper for creating virtual mappings
frame_allocator
Allocator for obtaining physical frames
Side Effects: - Maps heap region (0xFFFF_8000_4444_0000 to 0xFFFF_8000_4454_0000) - Initializes global heap allocator - Enables Rust alloc crate functionality (Vec, Box, String)
Panics: If frame allocation fails or mapping fails
Constants
pub const HEAP_START: usize = 0xFFFF_8000_4444_0000;
pub const HEAP_SIZE: usize = 1024 * 1024; // 1 MiB
Example usage
// After initializing mapper and frame allocator
memory::init_heap(&mut mapper, &mut frame_alloc);
// Now heap allocations work
extern crate alloc;
use alloc::vec::Vec;
let mut v = Vec::new();
v.push(1);
v.push(2);
.. note
Must be called before any heap allocations (Vec, Box, String).
Heap initialization is a one-time operation during boot.
Helper functions for physical-virtual address conversion
// Convert physical to virtual (kernel space)
pub fn phys_to_virt(phys: PhysAddr) -> VirtAddr {
}
// Convert virtual to physical (if in physical mapping region)
pub fn virt_to_phys(virt: VirtAddr) -> Option<PhysAddr> {
}
The task subsystem provides async-based task execution, scheduling primitives, and context switching. Current implementation is minimal (v0.0.6) with no preemptive scheduling.
TaskId
Unique identifier for tasks
pub struct TaskId(pub u64);
impl TaskId { }
Thread Safety:
Uses atomic counter, safe to call from multiple contexts
Example usage
let task_id = TaskId::new(); serial_println!("Created task ID: {}", task_id.as_u64());
## Task State
Task lifecycle states
pub enum TaskState { }
## Scheduling Classes
Scheduling policies and priorities
pub enum SchedClass { }
impl Default for SchedClass { }
## CPU Context
Complete CPU state for context switching
[repr(C)] [derive(Debug, Clone, Copy)] pub struct CPUContext { }
## Task Control Block
TaskCB
Represents a schedulable task
pub struct TaskCB {
}
impl TaskCB {
}
new() ^^^^^
Create new task control block
pub fn new(
) -> Self
Parameters: name Human-readable task name for debugging
entry_point
Function to execute when task runs, must never return
stack
Top of kernel stack for this task
sched_class
Scheduling policy and priority
Returns: Initialized TaskCB in Ready state
Example usage
extern "C" fn my_task() -> ! {
}
let stack = VirtAddr::new(0xFFFF_8000_0001_0000);
let task = TaskCB::new(
);
Builder pattern for task creation
pub struct TaskBuilder {
}
impl TaskBuilder {
}
Example usage
let task = TaskBuilder::new("background_worker")
Scheduler::global().lock().add_task(task);
Global task scheduler
pub struct Scheduler {
}
impl Scheduler {
}
init_global()
Initialize global scheduler instance
pub fn init_global()
Must be called once during kernel initialization before any tasks are created.
Example
Scheduler::init_global();
global()
Access global scheduler
pub fn global() -> &'static spin::Mutex<Scheduler>
Returns: Reference to global scheduler protected by spinlock
Example
let mut sched = Scheduler::global().lock();
sched.add_task(task);
serial_println!("Task count: {}", sched.task_count());
add_task()
Add task to scheduler run queue
pub fn add_task(&mut self, task: TaskCB)
## Context Switching
context_switch()
Low-level context switch between tasks
[naked]
pub unsafe extern "C" fn context_switch(
)
Parameters: old Pointer to save current CPU context
new
Pointer to load new CPU context
Safety: - Must be called with valid context pointers - Pointers must remain valid for entire operation - Should only be called by scheduler
Example (internal scheduler use)
let old_ctx = &mut tasks[current_idx].context as *mut CPUContext;
let new_ctx = &tasks[next_idx].context as*const CPUContext;
unsafe {
}
task_yield()
Voluntarily yield CPU to another task
pub fn task_yield()
Example
extern "C" fn cooperative_task() -> ! { }
## System Calls
The syscall subsystem provides the interface between userspace programs and
the kernel. Currently implements 10 syscalls.
## Syscall Numbers
Syscall vector assignments
const SYS_READ: u64 = 0; const SYS_WRITE: u64 = 1; const SYS_OPEN: u64 = 2; const SYS_CLOSE: u64 = 3; const SYS_SEEK: u64 = 8; const SYS_SEND: u64 = 20; const SYS_RECV: u64 = 21; const SYS_RECV_BLOCK: u64 = 22; const SYS_YIELD: u64 = 24; const SYS_EXIT: u64 = 60;
## Calling Convention
Syscalls use the SYSCALL instruction (x86_64 fast syscall mechanism)
rax Syscall number rdi Argument 1 rsi Argument 2 rdx Argument 3 r10 Argument 4 (rcx is clobbered by SYSCALL) r8 Argument 5 r9 Argument 6
Return value in rax
## serix_write()
Write bytes to file descriptor
pub fn serix_write(fd: u64, buf: *const u8, count: u64) -> u64
Parameters:
fd
File descriptor (1 = stdout, 2 = stderr)
buf
Pointer to data buffer
count
Number of bytes to write
Returns:
Number of bytes written, or error code
Example (userspace)
use ulib::serix_write;
let msg = b"Hello from userspace\n"; let written = serix_write(1, msg.as_ptr(), msg.len() as u64);
Kernel Handler
// kernel/src/syscall.rs fn sys_write(fd: u64, buf: u64, count: u64) -> u64 { }
## serix_read()
Read bytes from file descriptor
pub fn serix_read(fd: u64, buf: *mut u8, count: u64) -> u64
Parameters:
fd
File descriptor (0 = stdin)
buf
Pointer to buffer for data
count
Maximum bytes to read
Returns:
Number of bytes read, or error code
Example (userspace)
use ulib::serix_read;
let mut buf = [0u8; 128]; let read_count = serix_read(0, buf.as_mut_ptr(), 128);
## serix_exit()
Terminate current task
pub fn serix_exit(code: u64) -> !
Parameters:
code
Exit status code
Never returns (task is terminated).
Example (userspace)
use ulib::serix_exit;
serix_exit(0); // Success
## serix_yield()
Yield CPU to scheduler
pub fn serix_yield()
Voluntarily gives up remaining timeslice to allow other tasks to run.
Example (userspace)
use ulib::serix_yield;
loop { }
## serix_open()
Open a file by path
pub fn serix_open(path: &str) -> isize
Parameters:
path
Absolute path (e.g. "/hello.txt")
Returns:
File descriptor (>= 3) on success, or negative errno on error
Error Codes:
ENOENT (-2): Path not found
EFAULT (-14): Invalid pointer
EINVAL (-22): Invalid UTF-8 path
Example (userspace)
use ulib::serix_open;
let fd = serix_open("/hello.txt"); if fd >= 3 { // fd is valid, use with read/write/close }
## serix_close()
Close a file descriptor
pub fn serix_close(fd: usize) -> isize
Parameters:
fd
File descriptor to close
Returns:
0 on success, EBADF if fd not found
Example (userspace)
use ulib::serix_close;
serix_close(fd);
## serix_seek()
Set file read/write cursor position
pub fn serix_seek(fd: usize, offset: usize) -> isize
Parameters:
fd
File descriptor
offset
New byte offset from start of file
Returns:
0 on success, EBADF if fd not found
Example (userspace)
use ulib::serix_seek;
serix_seek(fd, 0); // Seek back to start
## Interrupt Management
The IDT subsystem loads interrupt handlers and manages CPU exceptions.
## IDT Initialization
init_idt()
Load Interrupt Descriptor Table into CPU
pub fn init_idt()
Side Effects: - Loads IDT into IDTR register - Marks IDT as loaded globally
Must be called during kernel initialization before enabling interrupts.
Example usage
idt::init_idt();
x86_64::instructions::interrupts::enable(); // Now safe
Panics: None (will triple fault if IDT entries are invalid)
register_interrupt_handler()
Register or update interrupt handler after IDT is loaded
pub fn register_interrupt_handler( )
Parameters:
vector
Interrupt vector number (32-255 for hardware interrupts)
handler
Interrupt handler function following x86-interrupt calling convention
Safety:
Handler must follow x86-interrupt ABI
Example usage
extern "x86-interrupt" fn custom_handler(_frame: InterruptStackFrame) { }
unsafe { }
Reloads IDT automatically if already loaded.
## Exception Handlers
Pre-registered CPU exception handlers:
Divide by Zero (Vector 0)
extern "x86-interrupt" fn divide_by_zero_handler(_stack: InterruptStackFrame)
Triggered By: Division by zero or division overflow
Action: Calls util::panic::oops("Divide by Zero exception")
Page Fault (Vector 14)
extern "x86-interrupt" fn page_fault_handler( )
Triggered By:
Invalid memory access (not present, protection violation, reserved bits)
Information Provided:
- Faulting address (from CR2 register)
- Error code (present, write, user, reserved, instruction fetch flags)
- Instruction pointer from stack frame
Action:
Logs fault details to serial console and halts
Double Fault (Vector 8)
extern "x86-interrupt" fn double_fault_handler(
) -> !
Triggered By: Exception during exception handling (system in inconsistent state)
Action: Panics immediately, never returns
Keyboard (Vector 33)
extern "x86-interrupt" fn keyboard_interrupt_handler(_frame: InterruptStackFrame)
IRQ:
IRQ1 (PS/2 keyboard controller)
Action:
1. Read scancode from I/O port 0x60
2. Call keyboard::handle_scancode(scancode)
3. Send EOI to APIC
## APIC Management
The APIC subsystem manages the Advanced Programmable Interrupt Controller,
including Local APIC, I/O APIC, and LAPIC timer.
## Local APIC
enable()
Enable Local APIC and disable legacy PIC
pub unsafe fn enable()
Side Effects: - Sets APIC Global Enable bit in IA32_APIC_BASE MSR - Sets APIC Software Enable bit in SVR register - Remaps and masks all legacy PIC interrupts
Must be called before using any APIC functionality.
Example usage
unsafe {
}
send_eoi()
Signal End of Interrupt to Local APIC
pub unsafe fn send_eoi()
Must be called at end of every hardware interrupt handler (not CPU exceptions).
Example usage
extern "x86-interrupt" fn interrupt_handler(_frame: InterruptStackFrame) {
}
set_timer()
Configure Local APIC timer (low-level interface)
pub unsafe fn set_timer(vector: u8, divide: u32, initial_count: u32)
Parameters: vector Interrupt vector number (32-255)
divide
Divider configuration value (0-11)
initial_count
Timer period in bus cycles
Example usage
unsafe {
}
Note: Prefer using apic::timer::init_hardware() for standard timer configuration.
init_ioapic()
Initialize I/O APIC with default IRQ routing
pub unsafe fn init_ioapic()
Default Mappings:
- IRQ0 (PIT timer) → Vector 32
- IRQ1 (keyboard) → Vector 33
Example usage
unsafe { }
map_irq()
Map hardware IRQ to interrupt vector
pub unsafe fn map_irq(irq: u8, vector: u8)
Parameters: irq Hardware IRQ number (0-23)
vector
Interrupt vector number (32-255)
Example usage
unsafe {
}
register_handler()
Register timer interrupt handler with IDT
pub unsafe fn register_handler()
Must be called before idt::init_idt().
Example usage
unsafe { } idt::init_idt();
init_hardware()
Configure and start LAPIC timer hardware
pub unsafe fn init_hardware()
Must be called after idt::init_idt() and interrupts enabled.
Configuration: - Vector: 49 (0x31) - Mode: Periodic - Divider: 16 - Frequency: ~625 Hz
Example usage
idt::init_idt();
x86_64::instructions::interrupts::enable();
unsafe {
}
ticks()
Get timer tick count since boot
pub fn ticks() -> u64
Returns:
Number of timer interrupts since kernel started
Thread Safety:
Safe to call (reads from static variable)
Example usage
let start = apic::timer::ticks(); do_work(); let end = apic::timer::ticks(); serial_println!("Work took {} ticks", end - start);
## Hardware Abstraction Layer
## Serial Console
init_serial()
Initialize COM1 serial port
pub fn init_serial()
Configuration: - Port: COM1 (0x3F8) - Baud rate: 38400 - Data bits: 8 - Stop bits: 1 - Parity: None
Must be called first during kernel initialization for debug output.
serial_println!()
Print line to serial console
serial_println!("format string", args...)
Macro for formatted output to serial port. Safe to call from interrupt context.
Example usage
serial_println!("Kernel booting..."); serial_println!("APIC ID: {}", apic_id);
## Utility Functions
## Panic Handling
oops()
Handle kernel oops (non-recoverable error)
pub fn oops(msg: &str) -> !
Parameters: msg Error message to display
Behavior: - Prints message with [KERNEL OOPS] prefix to serial console - Enters infinite halt loop - Never returns
Example usage
if !is_valid(ptr) {
}
Use Cases: - CPU exceptions (from exception handlers) - Hardware errors - Assertion failures - Unrecoverable kernel state corruption
halt_loop()
Enter infinite halt loop (low power)
pub fn halt_loop() -> !
Behavior:
- Executes HLT instruction in infinite loop
- CPU wakes on interrupt, then halts again
- Never returns
Power Efficiency:
Uses ~1% CPU vs busy-wait loop at 100%
Example usage
serial_println!("System halted"); halt_loop();
## Virtual Filesystem
The VFS subsystem provides filesystem abstraction with a trait-based INode
interface, global root directory, path resolution, and FAT32 backend.
## VFS Root and Path Resolution
set_root()
Set the global VFS root inode
pub fn set_root(root: Arc<dyn INode>)
Parameters: root Root directory inode (typically FatDirINode::root() or RamDir)
Must be called once during boot after filesystem mount.
lookup_path()
Resolve an absolute path to an inode
pub fn lookup_path(path: &str) -> Option<Arc>
Parameters:
path
Absolute path (e.g. "/hello.txt", "/subdir/file.txt", or "/")
Returns:
Some(inode) if path resolves successfully, None otherwise
Algorithm:
Splits path by '/' and iteratively calls INode::lookup() on each component
starting from VFS_ROOT.
## INode Trait
pub trait INode: Send + Sync { fn read(&self, offset: usize, buf: &mut [u8]) -> usize; fn write(&self, offset: usize, buf: &[u8]) -> usize; fn metadata(&self) -> FileType; fn lookup(&self, name: &str) -> Option<Arc>; fn insert(&self, name: &str, node: Arc) -> Result<(), &'static str>; fn size(&self) -> usize; }
Implementations:
RamFile — In-memory file with Mutex<Vec<u8>> storage
RamDir — In-memory directory with Vec<(String, Arc<dyn INode>)> children
FatDirINode — FAT32 directory backed by cluster chain on disk
FatFileINode — FAT32 file backed by cluster chain on disk
## File Descriptor Table
// kernel/src/fd.rs pub fn open(task_id: u64, path: &str) -> Option pub fn close(task_id: u64, fd: u64) -> bool pub fn get(task_id: u64, fd: u64) -> Option<Arc> pub fn seek(task_id: u64, fd: u64, offset: usize) -> bool
Storage:
Global BTreeMap<(task_id, fd), Arc<OpenFile>> protected by spin::Mutex
OpenFile:
inode: Arc<dyn INode> — backing VFS node
offset: Mutex<usize> — current read/write cursor
FD Allocation:
FDs 0-2 reserved (stdin/stdout/stderr)
User files start at fd 3 (monotonically increasing)
## Boot Sequence
## Initialization Order
Critical subsystem initialization sequence
-
HAL (Hardware Abstraction Layer)
-
APIC (Interrupt Controller)
-
IDT (Interrupt Handlers)
-
Enable Interrupts
-
Timer Start
-
Memory (Paging and Heap)
-
Graphics
-
VFS
-
Scheduler
.. warning
Heap must be initialized before any allocations (Vec, Box, String). Interrupts must be enabled after IDT is loaded. Serial console should be initialized first for debug output.
## Data Flow Examples
Keyboard Input Flow
PS/2 Hardware → I/O Port 0x60 → I/O APIC → LAPIC
Keyboard Interrupt (Vector 33, IDT)
keyboard::handle_scancode()
Timer Tick Flow
LAPIC Timer → Timer Interrupt (Vector 49, IDT) apic::timer::timer_interrupt()
Page Fault Flow
Invalid Memory Access → CPU Exception #14
idt::page_fault_handler()
Single-Core Assumptions: Current Serix assumes single-core BSP only: - No SMP support - No true parallel execution - Interrupts provide only concurrency
Spin Mutex
use spin::Mutex;
static DATA: Mutex = Mutex::new(0);
// Usage let mut data = DATA.lock(); *data += 1; // Lock automatically released when guard drops
Once Initialization
use spin::Once;
static INIT: Once<MyStruct> = Once::new();
fn get_instance() -> &'static MyStruct {
}
Disable interrupts for critical sections
x86_64::instructions::interrupts::without_interrupts( || {
}); // Interrupts restored here
use x86_64::{VirtAddr, PhysAddr};
use x86_64::structures::paging::{Page, PhysFrame, Size4KiB};
use x86_64::structures::idt::InterruptStackFrame;
// Physical memory mapping
const PHYS_MEM_OFFSET: u64 = 0xFFFF_8000_0000_0000; // HHDM offset
// Kernel heap
const HEAP_START: usize = 0xFFFF_8000_4444_0000;
const HEAP_SIZE: usize = 1024 * 1024; // 1 MiB
const MAX_BOOT_FRAMES: usize = 65536;
const APIC_BASE: u64 = 0xFEE00000; // Local APIC MMIO base
const IOAPIC_BASE: u64 = 0xFEC00000; // I/O APIC MMIO base
// CPU exceptions: 0-31
const DIV_BY_ZERO_VECTOR: u8 = 0;
const DEBUG_VECTOR: u8 = 1;
const PAGE_FAULT_VECTOR: u8 = 14;
const DOUBLE_FAULT_VECTOR: u8 = 8;
// Hardware interrupts: 32-255
const PIT_TIMER_VECTOR: u8 = 32; // Legacy (disabled)
const KEYBOARD_VECTOR: u8 = 33; // PS/2 keyboard
const VIRTIO_BLK_VECTOR: u8 = 34; // VirtIO block device
const TIMER_VECTOR: u8 = 49; // LAPIC timer
Rust (default)
Default for Rust-to-Rust calls
pub fn rust_function(arg: u32) -> u64 { }
C
For bootloader interface and assembly interop
pub extern "C" fn c_function(arg: u32) -> u64 { }
x86-interrupt
For CPU exception and hardware interrupt handlers
extern "x86-interrupt" fn handler(frame: InterruptStackFrame) { }
extern "x86-interrupt" fn handler_with_error(
) { }
Panic
Used for unrecoverable programming errors
value.expect("Description of requirement") value.unwrap() panic!("Error message")
Oops
Used for hardware/CPU exceptions
util::panic::oops("Exception description")
Option
Used for nullable values
if let Some(value) = optional_value { } else { }
## Future: Result-Based APIs
Planned for post-v1.0
pub enum KernelError { }
pub type KernelResult = Result<T, KernelError>;
pub fn allocate_frame() -> KernelResult { }
## Future Extensions
## Planned API Additions
Memory Management
- deallocate_frame() - Free physical frames
- map_range() - Map multiple pages at once
- protect_page() - Change page permissions (RO, RW, NX)
- Memory statistics and pressure tracking
Task Management
- Preemptive scheduling with timer-based preemption
- task_sleep() - Sleep for duration
- task_block() - Block on condition variable
- task_wake() - Wake blocked task
- Process and thread creation API
- Priority inheritance for mutexes
Interrupt Management
- register_irq_handler() - High-level IRQ registration
- mask_irq() / unmask_irq() - Dynamic IRQ control
- Interrupt statistics and profiling
- MSI/MSI-X support
Device Management
- Device driver registration framework
- IOMMU support
- Power management (ACPI)
System Calls
- File I/O: stat
- Process management: fork, exec, wait, kill
- Memory: mmap, munmap, mprotect, brk
- IPC: pipe, socket, sendmsg, recvmsg
- Signals: sigaction, kill, sigreturn
Current Status: Pre-alpha (v0.0.6), APIs subject to change without notice
Versioning: Not yet established, will follow semantic versioning post-v1.0
Deprecation Policy: Not yet established
API Review: Comprehensive review required before v1.0 release
- MEMORY_LAYOUT.md - Complete memory map documentation
- CONTRIBUTING.md - Development guidelines
- Limine Protocol: https://github.com/limine-bootloader/limine/blob/trunk/PROTOCOL.md
- Boot Process - Kernel initialization and userspace setup
- Architecture Overview - System design and philosophy
- Kernel Module - Syscall implementation details
- Memory Layout - Address space organization