Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Getting Started

This document provides an overview of the Kyronix’s kernel and instructions for building and running it.

What is Kyronix’s kernel

Kyronix’s kernel(aka k9) is a POSIX-like operating system kernel for the x86_64 architecture. It implements a hybrid design with a custom jail-based sandboxing subsystem, lwIP-based networking, and an ext2 root filesystem.

Key Features

  • Limine v3 boot protocol support (BIOS and UEFI)
  • Lock-free physical memory allocator (LL-Free)
  • Virtual memory with 4-level page tables and demand paging
  • Process management with round-robin scheduling
  • SMP support (up to MAX_CPUS cores)
  • POSIX signals, file descriptors, and process management
  • Jail-based sandboxing with filesystem, PID, IPC, and privilege isolation
  • lwIP TCP/IP stack over virtio-net
  • ext2, FAT32, and CPIO filesystems
  • ChaCha20-based CSPRNG
  • Kernel memory leak detector (kmemleak)

Quick Start

  1. Build the kernel and bootable ISO:
make iso
  1. Run in QEMU with KVM acceleration:
make run
  1. For serial-only output:
make run-serial

Architecture

The kernel targets x86_64 and uses the following design:

  • Boot: Limine v3 protocol provides memory map, framebuffer, HHDM, RSDP, and kernel address information.
  • Memory: LL-Free lock-free allocator for physical frames; 4-level page tables for virtual memory; first-fit heap allocator.
  • Scheduling: Per-CPU round-robin with lock-free bitmap scanning via g_ready_mask.
  • Syscalls: Linux x86_64 ABI-compatible syscall interface via SYSCALL/SYSRET.
  • Networking: lwIP stack connected to virtio-net with static QEMU user-mode IP (10.0.2.15/24).

Directory Structure

PathDescription
kernel/Kernel source (arch, mm, fs, drivers, syscall, proc, net)
user/Userspace programs (shell, utilities)
rootfs/Root filesystem template
scripts/Build scripts and Kconfig tools
limine/Limine bootloader files
dist/Build output directory

Testing

Run the full test suite:

make test-iso && make test-run-log

This boots the kernel with a testrunner init program, executes test binaries, and reports pass/fail status via serial output. Memory leak detection is performed via the kmemleak subsystem.

Last reviewed: 2026-07-22

Building

This document describes the build process for the Kyronix’s kernel (codename k9). It is the root of the Building section, linking to containerized and native build methods.

Prerequisites

The kernel targets the x86_64 architecture and requires the following toolchain:

  • C11 compiler (gcc or x86_64-elf-gcc)
  • GNU ld (or x86_64-elf-ld)
  • make
  • xorriso (for ISO generation)
  • mkfs.ext2 (for disk image creation)
  • cpio (for initrd packaging)

The build system uses Kconfig for kernel configuration. The generated header kernel/config.h is auto-created from kernel/Kconfig via scripts/kconfig/conf.

Build Targets

TargetDescription
make allBuilds kernel ELF, initrd, and disk image (default)
make isoBuilds persistent bootable ISO
make live-isoBuilds live ISO (no persistent disk)
make test-isoBuilds test ISO with testrunner initrd
make test-diskCreates a 16 MiB ext2 test disk image
make test-initrdBuilds test initrd with userspace test suite
make kallsymsRegenerates the kernel symbol table from kernel.elf
make nconfigOpens interactive ncurses-based kernel configuration

Build Output

ArtifactPath
Kernel ELFdist/kernel.elf
Persistent ISOdist/kkyronix-<VERSION>-INDEV-amd64.iso
Live ISOdist/kkyronix-<VERSION>-INDEV-amd64-live.iso
Test ISOdist/kkyronix-<VERSION>-INDEV-amd64-test.iso
Initrddist/initrd.cpio
Disk imagedist/disk.img

Compiler Flags

The kernel is compiled with the following critical flags:

  • -std=c11 -O2 -ffreestanding -fno-stack-protector
  • -m64 -march=x86-64 -mno-sse -mno-sse2 -mno-mmx -mno-80387
  • -mno-red-zone -mcmodel=kernel
  • -fno-pic -fno-pie -fno-omit-frame-pointer

IMPORTANT: SSE and MMX are disabled in C flags but enabled at runtime via cpu_enable_sse() for FPU context switching. The linker script is linker.ld.

Container Builds

The Makefile supports building inside a container (Podman or Docker) by setting the CRUNTIME variable. See with-cbuildrt, with Docker, or without containers for details.

make all CRUNTIME=podman
make iso CRUNTIME=docker

QEMU Run Targets

TargetDescription
make runGraphical QEMU with KVM, virtio-net, AHCI disk
make run-serialSerial-only QEMU (no display)
make run-diskDirect disk boot (no ISO)
make run-uefiUEFI boot with OVMF
make live-runLive session from live ISO
make test-runTest runner in QEMU

NOTE: Run targets do NOT rebuild. Execute make iso (or equivalent) first.

Testing

The test framework uses a custom testrunner init that boots the kernel, runs test binaries from the test initrd, and reports results via serial output. Memory leak detection is performed via kmemleak during test runs.

make test-iso && make test-run-log

The test-run-log target captures serial output to test.log and checks for ALL TESTS PASSED and zero KMEMLEAK reports.

Last reviewed: 2026-07-22

Building with cbuildrt

This document describes how to build the Kyronix kernel using cbuildrt. It is the child of Building.

Overview

cbuildrt is a container-based build runtime that provides reproducible build environments. The Kyronix kernel build system supports cbuildrt as an alternative to Docker or Podman for containerized builds.

Status

This document is a placeholder. The cbuildrt integration is not yet present in the kernel source tree. See with Docker/Podman for the current containerized build method, or without containers for native builds.

Last reviewed: 2026-07-22

Building with Docker or Podman

This document describes how to build the Kyronix kernel inside a Docker or Podman container. This is the recommended method for reproducible builds.

Overview

The containerized build uses the Containerfile at the repository root, based on Alpine Linux 3.20. The container image includes all required build dependencies: gcc, make, binutils, nasm, bison, flex, cpio, e2fsprogs, xorriso, and others.

Usage

  1. Build any target with the CRUNTIME variable set:
make iso CRUNTIME=podman
make all CRUNTIME=docker
  1. The Makefile automatically builds the container image from Containerfile if it does not exist or if the Containerfile has been modified since the image was last built.

  2. The source tree is mounted at /src inside the container. All build artifacts are written to the host filesystem.

Supported Runtimes

RuntimeVariableDefault
PodmanCRUNTIME=podmanYes (default in Makefile)
DockerCRUNTIME=dockerNo

Container Image

The image is tagged kyronix-build:latest by default. Override via CONTAINER_IMAGE and CONTAINER_IMAGE_TAG variables.

make iso CRUNTIME=docker CONTAINER_IMAGE=custom-build CONTAINER_IMAGE_TAG=v1

Cleanup

make clean CRUNTIME=podman

This removes the dist/, build/, and iso_root/ directories on the host, plus rebuilds the container image on the next build.

NOTE: The container image is cached between builds. Only rebuilds when Containerfile changes.

Last reviewed: 2026-07-22

Building Without Containers

This document describes how to build the Kyronix kernel natively on the host system without containers.

Required Packages

Install the following packages for your distribution:

  • Alpine: gcc g++ make binutils musl-dev linux-headers nasm bison flex ncurses-dev cpio e2fsprogs xorriso
  • Debian/Ubuntu: gcc g++ make binutils nasm bison flex libncurses-dev cpio e2fsprogs xorriso
  • Arch: gcc make binutils nasm bison flex ncurses cpio e2fsprogs xorriso

Build Steps

  1. Ensure gcc (or x86_64-elf-gcc) and ld (or x86_64-elf-ld) are in PATH.
  2. Run the desired target without CRUNTIME:
make all
make iso
  1. The Kconfig toolchain (scripts/kconfig/conf) is built automatically from source on first run.

Cross-Compilation

If x86_64-elf-gcc and x86_64-elf-ld are found in PATH, the build system uses them automatically. Otherwise, it falls back to the system gcc and ld.

Kernel Configuration

Edit kernel options interactively:

make nconfig

This opens an ncurses interface. Configuration is stored in .config and generates kernel/config.h via Kconfig autoheader.

Formatting

Format all kernel C source files:

make fmt

Check formatting without modifying files:

make fmt-check

Formatting uses clang-format with the project’s .clang-format style file.

Last reviewed: 2026-07-22

Contributing

This document provides guidelines for contributing to the Kyronix kernel. It is the root of the Contributing section.

Subsections

  1. Overview
  2. Commit Messages
  3. Coding Style
  4. AI Policy

Last reviewed: 2026-07-22

Contributing Overview

This document provides an overview of the contribution workflow for the Kyronix kernel. It is the child of Contributing.

Workflow

  • The project uses a standard fork-and-pull workflow.
  • All code must pass make fmt-check (clang-format) before submission.
  • The kernel is written in C11 with the project’s .clang-format style.
  • Assembly files use AT&T syntax with Intel intrinsics.
  • The project uses Kconfig for kernel configuration.

Last reviewed: 2026-07-22

Commit Messages

This document describes the commit message conventions for the Kyronix kernel. It is the child of Contributing.

Format

  1. Commit messages must follow the conventional format.
  2. The first line is a short summary (50-72 characters).
  3. A blank line separates the summary from the body.
  4. The body explains what and why, not how.
  5. Reference issues with #NNN.

Last reviewed: 2026-07-22

Coding Style

This document describes the coding style conventions for the Kyronix kernel. It is the child of Contributing.

Conventions

  • Formatting: enforced by clang-format with the project’s .clang-format file.
  • Run make fmt to format all kernel source files.
  • Run make fmt-check to verify formatting without changes.
  • Naming: snake_case for functions and variables, UPPER_CASE for macros and constants.
  • Headers: include what you use, prefer forward declarations.
  • Error handling: check return values, use goto-based cleanup.
  • Memory: always check allocation results, free on error paths.
  • Assembly: AT&T syntax for .S files, Intel intrinsics via <stdint.h>.

Last reviewed: 2026-07-22

AI Policy

This document describes the AI usage policy for the Kyronix kernel project. It is the child of Contributing.

Rules

  • AI-generated code must be clearly attributed in commit messages.
  • Contributors must understand and be able to explain all code they submit, regardless of how it was generated.
  • AI tools may be used for code review, documentation, and research.
  • All submissions must be reviewed by a human maintainer before merging.

Last reviewed: 2026-07-22

System Architecture

This document describes the high-level architecture of the Kyronix operating system. It is the root of the System Architecture section.

System Layers

The Kyronix system consists of the following layers, from lowest to highest:

  1. Kernel – Hardware abstraction, memory management, process scheduling, syscalls
  2. Drivers – PCI, ACPI, AHCI, VirtIO, input, framebuffer, TTY, serial
  3. Filesystems – VFS, ext2, procfs, devfs
  4. Networking – lwIP TCP/IP stack, virtio-net interface

Design Principles

  • The kernel is hybrid: all basic subsystems run in kernel space with direct hardware access but selected drivers can run in userspace.
  • Syscalls follow the Linux x86_64 ABI for compatibility with Linux user-space programs.
  • Jail-based sandboxing provides process isolation without requiring separate address spaces.
  • The physical memory allocator is lock-free (LL-Free) for scalability on SMP systems.

Last reviewed: 2026-07-22

Components

This document lists the major components of the Kyronix kernel and their source locations.

Kernel Subsystems

ComponentSource PathDescription
Architecture (x86_64)kernel/arch/x86_64/CPU primitives, GDT, IDT, LAPIC, PIT, syscall entry
Memory Managementkernel/mm/PMM (LL-Free), VMM, VMA, heap, shared memory
Process Managementkernel/proc/Process table, scheduler, SMP, signals, jails
Filesystemskernel/fs/VFS, ext2, FAT32, procfs, devfs, pipes, sockets
Driverskernel/drivers/PCI, ACPI, AHCI, VirtIO, input, framebuffer, TTY, serial
Syscallskernel/syscall/Linux ABI-compatible syscall dispatcher and handlers
Networkingkernel/net/lwIP integration, virtio-net interface
Cryptographykernel/crypto/ChaCha20 CSPRNG
Executable Loadingkernel/exec/ELF loader, process exec, stack setup
Bootkernel/boot/Limine protocol definitions

Libraries

LibrarySource PathDescription
Stringkernel/lib/string.clibc-style string functions
Printfkernel/lib/printf.cKernel printf implementation
Logkernel/lib/log.cKernel logging (log_info, log_warn)
Kallsymskernel/lib/kallsyms.cKernel symbol lookup

Last reviewed: 2026-07-22

Kernel

This document describes the Kyronix kernel subsystem. It is the child of System Architecture and parent of all kernel-specific architecture documents.

Kernel Overview

The Kyronix kernel (codename k9) is a monolithic kernel for x86_64, version 0.2. It boots via the Limine v3 protocol and initializes the following subsystems in order:

  1. Serial output and printf setup
  2. GDT and IDT
  3. Physical Memory Manager (PMM) with LL-Free lock-free allocator
  4. Virtual Memory Manager (VMM) with 4-level page tables
  5. Local APIC and SMP
  6. Kernel heap allocator
  7. Syscall entry (SYSCALL/SYSRET)
  8. Process scheduler
  9. Jail sandboxing
  10. Virtual Filesystem (VFS) with /proc, /sys, /dev/pts mounts
  11. PCI enumeration, ACPI, AHCI, VirtIO-net
  12. Network stack (lwIP)
  13. PIT timer and LAPIC timer calibration
  14. Application Processor (AP) boot
  15. ChaCha20 CSPRNG
  16. Root filesystem mount and init exec

Kernel Entry Point

The kernel entry point is kmain() in kernel/kernel.c:255. It receives no arguments from the bootloader; all boot information is obtained via Limine protocol requests.

Sections

Last reviewed: 2026-07-22

Boot

This document describes the Kyronix kernel boot process. It is the child of Kernel and parent of the Limine protocol reference.

Boot Sequence

The Kyronix kernel boots via the Limine v3 boot protocol. The bootloader loads the kernel ELF and provides the following information through Limine requests:

  1. Memory map (LIMINE_MEMMAP_REQUEST) – Physical memory regions (usable, reserved, ACPI, framebuffer)
  2. HHDM offset (LIMINE_HHDM_REQUEST) – Higher Half Direct Map base address
  3. Framebuffer (LIMINE_FRAMEBUFFER_REQUEST) – Linear framebuffer for early display
  4. Kernel address (LIMINE_KERNEL_ADDRESS_REQUEST) – Physical and virtual base addresses
  5. RSDP (LIMINE_RSDP_REQUEST) – ACPI Root System Description Pointer
  6. Modules (LIMINE_MODULE_REQUEST) – Bootloader modules (initrd)

All requests use LIMINE_BASE_REVISION(3).

Boot Phases

Phase 1: Early Hardware (BSP)

  1. serial_init(COM1) – Initialize serial port for debug output
  2. gdt_init() – Set up Global Descriptor Table and Task State Segment
  3. idt_init() – Set up Interrupt Descriptor Table, remap PIC to vectors 32-47
  4. kbd_init() – Initialize keyboard driver

Phase 2: Memory Setup

  1. pmm_init() – Initialize physical memory manager from Limine memory map
  2. vmm_init() – Enable NX bit, initialize kernel page tables
  3. heap_init() – Initialize kernel heap allocator (64 KiB initial)

Phase 3: Per-CPU and SMP

  1. Write MSR_GS_BASE and MSR_KERNEL_GS_BASE for BSP per-CPU data
  2. smp_init() – Enumerate CPUs from Limine SMP response
  3. SMEP detection and enable via CPUID leaf 7
  4. syscall_init() – Configure SYSCALL/SYSRET MSRs, enable SSE

Phase 4: Device Drivers

  1. pci_enumerate() – Scan PCI bus
  2. acpi_init() – Parse ACPI tables from RSDP
  3. ahci_init() – Initialize SATA/AHCI controllers
  4. virtnet_init() – Initialize VirtIO network device
  5. net_init() – Initialize lwIP network stack

Phase 5: Timer and Scheduling

  1. pit_init() – Program PIT channel 0 at ~250 Hz
  2. lapic_calibrate_timer() – Calibrate LAPIC timer against PIT
  3. Create BSP idle process
  4. smp_boot_aps() – Wake Application Processors

Phase 6: Filesystem and Init

  1. ext2_init() – Register ext2 filesystem driver
  2. Mount root filesystem from disk or load initrd via CPIO
  3. process_exec("/init") – Load and execute init process

AP Boot Sequence

Application Processors are woken via the Limine SMP goto_address trampoline (ap_trampoline in kernel/proc/ap_trampoline.S). Each AP:

  1. Loads its cpu_local_t from limine_smp_info.extra_argument
  2. Loads the idle process kernel stack
  3. Calls ap_init_cpu() which initializes GDT, IDT, MSRs, SSE, SYSCALL, LAPIC
  4. Spins on g_kernel_ready until BSP signals completion
  5. Starts 250 Hz LAPIC periodic timer
  6. Enters ap_sched_loop() (idle/scheduling loop)

Status Output

During boot, each initialization step prints a status line:

* Initialising PMM ...                                              [ ok ]
* Initialising VMM ...                                              [ ok ]

The [ ok ] / [ !! ] indicator is right-aligned at column 72 using ANSI escape codes.

Last reviewed: 2026-07-22

Limine Protocol

This document describes the Limine v3 boot protocol interface used by the Kyronix kernel. It is the child of Boot.

Overview

The Kyronix kernel uses the Limine v3 boot protocol to obtain boot-time information from the bootloader. The protocol uses a request/response mechanism where the kernel declares requests in a special ELF section (.limine_requests), and the bootloader populates the response fields before jumping to the kernel entry point.

Request Types

RequestStructurePurpose
LIMINE_FRAMEBUFFER_REQUESTlimine_framebuffer_responseLinear framebuffer information
LIMINE_MEMMAP_REQUESTlimine_memmap_responsePhysical memory map
LIMINE_HHDM_REQUESTlimine_hhdm_responseHigher Half Direct Map offset
LIMINE_MODULE_REQUESTlimine_module_responseBootloader modules (initrd)
LIMINE_KERNEL_ADDRESS_REQUESTlimine_kernel_address_responseKernel physical/virtual addresses
LIMINE_RSDP_REQUESTlimine_rsdp_responseACPI RSDP physical address
LIMINE_SMP_REQUESTlimine_smp_responseSMP CPU information

Memory Map Types

TypeValueDescription
LIMINE_MEMMAP_USABLE0Available for kernel use
LIMINE_MEMMAP_RESERVED1Reserved by hardware/firmware
LIMINE_MEMMAP_ACPI_RECLAIMABLE2Usable after ACPI parsing
LIMINE_MEMMAP_ACPI_NVS3ACPI NVS memory (must not reclaim)
LIMINE_MEMMAP_BAD_MEMORY4Defective memory region
LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE5Usable after bootloader exits
LIMINE_MEMMAP_KERNEL_AND_MODULES6Kernel and module images
LIMINE_MEMMAP_FRAMEBUFFER7Linear framebuffer memory

HHDM Response

The limine_hhdm_response provides the offset field, which is the base address of the Higher Half Direct Map. All physical addresses can be converted to virtual via phys_to_virt(phys) = phys + g_hhdm_offset.

Kernel Address Response

The limine_kernel_address_response provides physical_base and virtual_base, used to compute the physical end of the kernel image for PMM initialization.

Module Response

The limine_module_response provides an array of limine_file structures. Kyronix uses the first module as the initrd (CPIO archive).

SMP Response

The limine_smp_response provides cpu_count and an array of limine_smp_info structures. Each entry contains processor_id, lapic_id, and goto_address (trampoline entry point for APs).

Request Declaration

Requests are declared as static volatile structures bracketed by LIMINE_REQUESTS_START_MARKER and LIMINE_REQUESTS_END_MARKER. The base revision is set via LIMINE_BASE_REVISION(3).

LIMINE_REQUESTS_START_MARKER;

LIMINE_BASE_REVISION(3);

static volatile struct limine_memmap_request mmap_req = {
    .id = LIMINE_MEMMAP_REQUEST,
    .revision = 0,
    .response = NULL,
};

LIMINE_REQUESTS_END_MARKER;

IMPORTANT: The response field must be initialized to NULL. The bootloader sets it to a valid pointer if the request is fulfilled.

Last reviewed: 2026-07-22

Architecture

This document describes the hardware abstraction layer for the Kyronix kernel. It is the child of Kernel and parent of architecture-specific documents.

Supported Architectures

The Kyronix kernel currently supports x86_64 (AMD64) only. The architecture-specific code lives in kernel/arch/x86_64/.

Architecture Components

ComponentSourceDescription
CPU Primitivesarch/x86_64/cpu.hI/O ports, MSRs, control registers, compiler attributes
GDTarch/x86_64/gdt.cGlobal Descriptor Table and Task State Segment
IDTarch/x86_64/idt.cInterrupt Descriptor Table and ISR dispatch
LAPICarch/x86_64/lapic.cLocal APIC MMIO, IPI, timer calibration
PITarch/x86_64/pit.cProgrammable Interval Timer and RTC
Syscall Setuparch/x86_64/syscall_setup.cSYSCALL/SYSRET MSRs, SSE, per-CPU data
Syscall Entryarch/x86_64/syscall_entry.SSYSCALL entry and userspace trampolines
IDT Stubsarch/x86_64/idt_stubs.SAssembly ISR entry/exit stubs

Last reviewed: 2026-07-22

x86-64 Architecture

This document describes the x86-64 architecture support in the Kyronix kernel. It is the child of Architecture and parent of x86-64-specific documents.

Files

FilePurpose
cpu.hCPU primitives, I/O ports, MSRs, control registers, data structures
gdt.cGDT creation, TSS initialization, per-CPU segments
idt.cIDT setup, PIC remapping, ISR dispatch
idt_stubs.SAssembly ISR entry/exit stubs, isr_stub_table
lapic.cLocal APIC initialization, IPI, timer calibration
pit.cPIT channel 0 programming, RTC epoch reading
syscall_setup.cSYSCALL/SYSRET configuration, SSE enable, per-CPU local data
syscall_entry.SSYSCALL entry point, enter_userspace trampolines

GDT Layout

SelectorEntryDescription
0x00NullNull descriptor
0x08Kernel code64-bit ring 0 executable
0x10Kernel data64-bit ring 0 writable
0x18User data64-bit ring 3 writable
0x20User code64-bit ring 3 executable
0x28 + n*0x10TSS for CPU nTask State Segment

IDT Vector Layout

VectorsSourceGate TypeDescription
0-31CPU exceptionsINT_GATE#DE through #SX
32-47PIC IRQ 0-15INT_GATELegacy PIC interrupts
0x80 (128)SYSCALLUSER_GATE (DPL=3)System call entry
224 (0xE0)LAPIC timerINT_GATEPer-CPU timer tick
255 (0xFF)LAPIC spuriousINT_GATESpurious interrupt

IST Usage

IST IndexStackAssigned To
116 KiB dedicatedDouble Fault (#8)
216 KiB dedicatedNMI (vector 2)

Per-CPU Data

Per-CPU data is accessed via the GS segment register. MSR_GS_BASE points to cpu_local_t for the current CPU. Key offsets:

OffsetFieldDescription
0kernel_rspKernel stack pointer (for SYSCALL entry)
8user_rspUser stack pointer (saved on SYSCALL)
16cpu_idCPU identifier
32currentCurrent proc_t pointer
40idleIdle process pointer

The swapgs instruction swaps between user and kernel GS bases on privilege transitions.

Last reviewed: 2026-07-22

CPU Primitives

This document describes the x86-64 CPU primitives used by the Kyronix kernel. It is the child of x86-64 Architecture.

Source

kernel/arch/x86_64/cpu.h

Compiler Attributes

AttributeDefinitionPurpose
NORETURN__attribute__((noreturn))Function never returns
PACKED__attribute__((packed))No struct padding
ALIGNED(n)__attribute__((aligned(n)))Alignment requirement
INLINEstatic inline __attribute__((always_inline))Force inlining
UNUSED__attribute__((unused))Suppress unused warnings

I/O Port Functions

FunctionDescription
outb(port, val)Write byte to I/O port
outw(port, val)Write 16-bit word to I/O port
outl(port, val)Write 32-bit dword to I/O port
inb(port)Read byte from I/O port
inw(port)Read 16-bit word from I/O port
inl(port)Read 32-bit dword from I/O port
io_wait()Write 0 to port 0x80 (delay)

Interrupt Control

FunctionDescription
cli()Disable interrupts
sti()Enable interrupts
hlt()Halt CPU until next interrupt
cpu_relax()pause instruction (spin-loop hint)
cpu_halt()Disable interrupts, halt forever (NORETURN)

MSR Access

FunctionDescription
rdmsr(msr)Read 64-bit MSR
wrmsr(msr, val)Write 64-bit MSR

Control Register Access

FunctionDescription
read_cr0() / write_cr0(val)CR0 (cache control, write protect)
read_cr2()CR2 (page fault linear address)
read_cr3() / write_cr3(val)CR3 (page table base)
read_cr4() / write_cr4(val)CR4 (SMEP, PGE, etc.)

IRQ Save/Restore

FunctionDescription
irq_save()Save RFLAGS, disable interrupts, return saved flags
irq_restore(flags)Restore RFLAGS to saved value

Data Structures

cpu_state_t (Interrupt/Syscall Frame)

184-byte packed structure pushed by ISR stubs:

OffsetFieldDescription
0x00-0x38r15-r8General purpose registers
0x40-0x78rbp, rdi, rsi, rdx, rcx, rbx, raxMore GPRs
0x80int_noInterrupt vector number
0x88error_codeCPU error code (or 0)
0x90ripReturn instruction pointer
0x98csCode segment
0xA0rflagsFlags register
0xA8rspStack pointer
0xB0ssStack segment

gdt_entry_t (8 bytes, packed)

FieldSizeDescription
limit_lowu16Segment limit bits 0-15
base_lowu16Base address bits 0-15
base_midu8Base address bits 16-23
accessu8Access byte
granularityu8Granularity + flags + limit bits 16-19
base_highu8Base address bits 24-31

idt_entry_t (16 bytes, packed)

FieldSizeDescription
offset_lowu16Handler offset bits 0-15
selectoru16Code segment selector
istu8Interrupt Stack Table index
type_attru8Gate type + DPL + present bit
offset_midu16Handler offset bits 16-31
offset_highu32Handler offset bits 32-63
zerou32Reserved (must be zero)

Last reviewed: 2026-07-22

GDT

This document describes the Global Descriptor Table (GDT) implementation in the Kyronix kernel. It is the child of x86-64 Architecture.

Source

kernel/arch/x86_64/gdt.c

Overview

The GDT provides segment descriptors for kernel and user code/data, plus per-CPU Task State Segment (TSS) descriptors. In long mode, segments are mostly flat (base=0, limit=4 GiB), but the TSS is essential for interrupt stack switching and I/O permission bitmap.

GDT Structure

typedef struct {
    gdt_entry_t entries[5];         // null, kcode, kdata, udata, ucode
    tss_desc_t  tss[MAX_CPUS];     // one TSS descriptor per CPU
} gdt_t;  // aligned(16)

Segment Values

EntrySelectorEncoded ValueMeaning
Kernel code0x080x00AF9A000000FFFF64-bit, ring 0, executable, readable
Kernel data0x100x00CF92000000FFFF64-bit, ring 0, writable
User data0x180x00CFF2000000FFFF64-bit, ring 3, writable
User code0x200x00AFFA000000FFFF64-bit, ring 3, executable, readable

TSS Structure

The TSS (104 bytes on x86-64) provides:

  • rsp0: Ring 0 stack pointer, used on privilege level transitions (interrupts from ring 3)
  • ist[7]: Interrupt Stack Table entries for critical handlers (Double Fault, NMI)
  • iopb_offset: Offset to I/O permission bitmap
typedef struct {
    uint32_t reserved0;
    uint64_t rsp0, rsp1, rsp2;
    uint64_t reserved1;
    uint64_t ist[7];
    uint64_t reserved2;
    uint16_t reserved3;
    uint16_t iopb_offset;  // = sizeof(tss_t)
} tss_t;  // 104 bytes, packed

Key Functions

FunctionDescription
gdt_init()Initialize GDT, BSP TSS, load GDT and TSS on BSP
gdt_ap_load(cpu_id)Load GDT and TSS on an Application Processor
gdt_set_kernel_stack(rsp0)Update TSS rsp0 for the current CPU
tss_init(tss)Initialize TSS fields (IST stacks, IOPB offset)

IST Stacks

IST IndexStackSizeAssigned To
1g_ist_df16 KiBDouble Fault (#8)
2g_ist_nmi16 KiBNMI (vector 2)

NOTE: IST stacks provide dedicated, non-overflowable stacks for the most critical exceptions, preventing kernel stack corruption during Double Faults.

Last reviewed: 2026-07-22

IDT

This document describes the Interrupt Descriptor Table (IDT) implementation in the Kyronix kernel. It is the child of x86-64 Architecture.

Source

kernel/arch/x86_64/idt.c, kernel/arch/x86_64/idt_stubs.S

Overview

The IDT provides 256 interrupt vector entries. The Kyronix kernel maps CPU exceptions (vectors 0-31), legacy PIC IRQs (vectors 32-47), the SYSCALL vector (0x80), LAPIC timer (224), and LAPIC spurious (255) through a table of 51 assembly stubs (isr_stub_table).

Vector Allocation

VectorsSourceGate TypeISTHandler
0-31CPU exceptionsINT_GATEisr_dispatch()
3 (#BP)BreakpointTRAP_GATEAllows debugger resume
8 (#DF)Double FaultINT_GATEIST 1Dedicated DF stack
2 (NMI)NMIINT_GATEIST 2Dedicated NMI stack
32-47PIC IRQ 0-15INT_GATEisr_dispatch()
128 (0x80)SYSCALLUSER_GATE (DPL=3)isr_dispatch()
224 (0xE0)LAPIC timerINT_GATEisr_dispatch()
255 (0xFF)LAPIC spuriousINT_GATESilently returns

Gate Types

ConstantValueDescription
IDT_INT_GATE0x8EInterrupt gate: present, DPL=0, clears IF on entry
IDT_TRAP_GATE0x8FTrap gate: present, DPL=0, does NOT clear IF
IDT_USER_GATE0xEEInterrupt gate: present, DPL=3 (user-accessible)

ISR Dispatch (isr_dispatch)

The central C function called from isr_common assembly after register save. It handles:

CPU Exceptions (vectors 0-31)

  • User-mode exceptions: Deliver signal to process via exception_signal(n) (SIGFPE, SIGILL, SIGTRAP, SIGSEGV, SIGBUS, etc.)
  • Page fault (vector 14): handle_user_page_fault() implements demand paging for stack growth and VMA-based anonymous pages
  • Breakpoint/Debug (vectors 1, 3): Check tracer_pid for ptrace support; #BP decrements RIP by 1 past int3
  • Kernel-mode exceptions: Full panic with register dump, exception name, CR2 (for #PF), and kernel backtrace

PIC IRQs (vectors 32-47)

  • IRQ 0 (timer tick): Increments g_ticks, cursor blink, zombie reaping, network polling, timer mask processing (wakeup, alarm, itimer), preemption check
  • IRQs 1-15: Dispatch to registered g_irq_handlers[irq] handlers

Special Vectors

  • LAPIC timer (224): Same preemption logic as PIC IRQ 0
  • LAPIC spurious (255): Silently returns

ISR Stub Assembly

The isr_common routine in idt_stubs.S performs:

  1. swapgs if entering from ring 3 (privilege transition)
  2. Push all 15 GPRs (forming cpu_state_t frame)
  3. Call isr_dispatch(state) in C
  4. Pop all GPRs
  5. swapgs if returning to ring 3
  6. iretq

IRQ Registration

void request_irq(uint8_t irq, irq_handler_fn fn, void *arg);

Registers a handler for PIC IRQ 0-15 and unmasks it via pic_unmask_irq().

Kernel Backtrace

The kernel_backtrace() function scans the kernel stack for return addresses in the range [0xffffffff80000000, 0xffffffff80040000) and prints up to 48 candidates. It is called only during kernel panics.

Last reviewed: 2026-07-22

LAPIC

This document describes the Local APIC implementation in the Kyronix kernel. It is the child of x86-64 Architecture.

Source

kernel/arch/x86_64/lapic.c

Overview

The Local APIC (Advanced Programmable Interrupt Controller) provides per-CPU interrupt handling, Inter-Processor Interrupts (IPIs), and the LAPIC timer. The MMIO registers are mapped at virtual address 0xfffffe0000000000.

Initialization

  1. Read IA32_APIC_BASE MSR to get physical MMIO address
  2. Enable LAPIC if disabled (set IA32_APIC_BASE_ENABLE)
  3. Map physical LAPIC to LAPIC_VIRT with VMM_KDATA | VMM_PCD (page-cache disabled for MMIO)
  4. Enable Spurious Vector Register (SVR) with spurious vector
  5. Mask error, thermal, performance, and timer LVT entries
  6. Clear Task Priority Register (TPR)
  7. Read LAPIC ID and version

Key Functions

FunctionDescription
lapic_init()Full LAPIC initialization and MMIO mapping
lapic_eoi()Write 0 to EOI register (end-of-interrupt)
lapic_send_ipi(lapic_id, icr_lo)Send IPI to specific LAPIC
lapic_send_ipi_self(icr_lo)Send self-IPI
lapic_calibrate_timer()Calibrate LAPIC timer against PIT
lapic_timer_start_periodic(hz)Start periodic timer at given frequency
lapic_timer_freq()Return calibrated timer frequency (ticks/ms)

Timer Calibration

The calibration algorithm:

  1. Set LAPIC timer to one-shot mode with divisor 0x0B and initial count 0xFFFFFFFF
  2. Count 5 PIT counter wraps (each wrap = one PIT period)
  3. Compute remaining = 0xFFFFFFFF - current_count
  4. Derive g_lapic_timer_freq = remaining / 5 (ticks per millisecond)

The LAPIC timer is then started in periodic mode at 250 Hz for scheduling ticks.

IPI Mechanism

Inter-Processor Interrupts are sent via the Interrupt Command Register (ICR):

  1. Wait for send-pending bit to clear
  2. Write target LAPIC ID to ICR_HI
  3. Write delivery info to ICR_LO
  4. Wait for send-pending to clear again

Register Layout

OffsetRegisterDescription
0x20TPRTask Priority Register
0x80EOIEnd of Interrupt
0xB0ICR_LOInterrupt Command (low)
0xC0ICR_HIInterrupt Command (high)
0xD0SVRSpurious Vector Register
0x320LVT TimerTimer LVT entry
0x350LVT LINT0LINT0 LVT entry
0x360LVT LINT1LINT1 LVT entry
0x370LVT ErrorError LVT entry

Last reviewed: 2026-07-22

PIT

This document describes the Programmable Interval Timer (PIT) implementation in the Kyronix kernel. It is the child of x86-64 Architecture.

Source

kernel/arch/x86_64/pit.c

Overview

The PIT provides the system tick source via channel 0. The CMOS Real-Time Clock (RTC) is read at boot to establish a Unix epoch base time.

PIT Configuration

SettingValue
Channel0
ModeSquare wave (mode 3)
Reload value4772
Frequency1193182 / 4772 = ~250.06 Hz
Tick interval~4 ms

Key Functions

FunctionDescription
pit_init()Program PIT channel 0, read RTC epoch, unmask PIC IRQ 0
pit_read_counter()Latch and read PIT channel 0 counter (used during LAPIC calibration)

RTC Reading

The CMOS RTC is read at boot to obtain the current date/time:

  1. Wait for UIP (Update In Progress) flag to clear (CMOS register 0x0A, bit 7)
  2. Read seconds, minutes, hours, day, month, year, century from CMOS registers
  3. Convert BCD to binary if needed (Status Register B, bit 2)
  4. Handle 12/24 hour mode (Status Register B, bit 1)
  5. Compute Unix timestamp: total seconds since 1970-01-01

The resulting g_epoch_base is used by sys_gettimeofday(), sys_clock_gettime(), and sys_time() to provide wall-clock time.

Global State

VariableTypeDescription
g_ticksvolatile uint64_tSystem tick counter (incremented on each IRQ 0)
g_epoch_baseuint64_tUnix timestamp at boot

NOTE: The PIT is the legacy timer source. The LAPIC timer is calibrated against it and provides the per-CPU scheduling tick at 250 Hz.

Last reviewed: 2026-07-22

Syscall Entry

This document describes the syscall entry mechanism in the Kyronix kernel. It is the child of x86-64 Architecture.

Sources

kernel/arch/x86_64/syscall_setup.c, kernel/arch/x86_64/syscall_entry.S

Overview

The Kyronix kernel uses the AMD64 SYSCALL/SYSRET mechanism for system calls. This provides a fast, non-interrupt-based transition between user mode (ring 3) and kernel mode (ring 0).

MSR Configuration

MSRValuePurpose
MSR_EFER (0xC0000080)SCE bit setEnable SYSCALL support
MSR_STAR (0xC0000081)Segments encodedUser CS/SS = 0x20/0x28, Kernel CS/SS = 0x08/0x18
MSR_LSTAR (0xC0000082)syscall_entrySYSCALL entry point address
MSR_SFMASK (0xC0000084)IF, TF, DF, ACRFLAGS bits cleared on SYSCALL

SYSCALL Entry Sequence

The syscall_entry label in syscall_entry.S:

  1. swapgs – switch GS to kernel per-CPU data
  2. Save user RSP to gs:CPU_USER_RSP
  3. Load kernel RSP from gs:CPU_KERNEL_RSP
  4. Push all 15 GPRs (forming a cpu_state_t-compatible frame)
  5. Move RSP to RDI (first argument = pointer to register frame)
  6. call syscall_dispatch (C function)
  7. Pop all GPRs in reverse
  8. Restore user RSP from gs:CPU_USER_RSP
  9. swapgs – switch GS back to user per-CPU data
  10. sysretq – return to ring 3

Userspace Trampolines

FunctionDescription
enter_userspace(rip, rsp, rflags)First entry to ring 3 (no swapgs)
enter_userspace_exec(rip, rsp, rflags)Entry after exec (includes swapgs)

Both functions zero all GPRs except RIP, RSP, and RFLAGS, then execute sysretq.

SSE/FPU Setup

SSE is enabled before SYSCALL configuration:

  1. Clear CR0.EM (bit 2) – disable emulation
  2. Set CR0.MP (bit 1) – monitor coprocessor
  3. Set CR4.OSFXSR (bit 9) – enable FXSAVE/FXRSTOR
  4. Set CR4.OSXMMEXCPT (bit 10) – enable unmasked SSE exceptions
  5. fninit + ldmxcsr 0x1F80 – initialize x87 and SSE defaults

Per-CPU Local Data

cpu_local_t is a 64-byte aligned structure accessed via GS:

typedef struct {
    uint64_t kernel_rsp;    // offset 0
    uint64_t user_rsp;      // offset 8
    uint32_t cpu_id;        // offset 16
    uint32_t lapic_id;      // offset 20
    uint32_t online;        // offset 24
    proc_t  *current;       // offset 32
    proc_t  *idle;          // offset 40
} cpu_local_t;

Last reviewed: 2026-07-22

Memory Management

This document describes the memory management subsystem of the Kyronix kernel. It is the child of Kernel and parent of memory management component documents.

Architecture

The memory management subsystem consists of four layers:

  • LL-Free (llfree.c) – Lock-free physical frame allocator
  • PMM (pmm.c) – Physical page allocation
  • VMM / VMA (vmm.c, vma.c) – Virtual memory and area tracking
  • Heap / SHM (heap.c, shm.c) – Kernel heap and shared memory

Components

ComponentSourceDescription
PMMmm/pmm.cPhysical page allocation via LL-Free
VMMmm/vmm.c4-level page table management
VMAmm/vma.cVirtual Memory Area tracking
Heapmm/heap.cKernel heap (first-fit linked list)
SHMmm/shm.cSysV shared memory (up to 64 segments)
LL-Freemm/llfree.cLock-free physical frame allocator
KmemLeakmm/kmemleak.cKernel memory leak detector

Key Constants

ConstantValueDescription
PAGE_SIZE4096Page frame size
PAGE_SHIFT12Bit shift for page-to-byte conversion
HEAP_START0xffff910000000000Heap virtual address base
HEAP_MAX0xffff920000000000Maximum heap address (4 GiB)
USER_LIMIT0x800000000000Top of user half (128 TiB)
VMM_MAX_SPACES256Maximum concurrent address spaces
VMM_VMA_MAX2048Maximum VMAs per address space

Last reviewed: 2026-07-22

PMM

This document describes the Physical Memory Manager (PMM) in the Kyronix kernel. It is the child of Memory Management.

Source

kernel/mm/pmm.c, kernel/mm/pmm.h

Overview

The PMM manages physical page frames using the LL-Free lock-free allocator. It provides allocation and deallocation of 4 KiB page frames, with support for contiguous multi-page allocations.

Key Constants

ConstantValueDescription
PAGE_SIZE4096Size of one page frame
PAGE_SHIFT12Bits to shift for page count
ZPOOL_SIZE32Pre-allocated zeroed page pool size

Macros

MacroDescription
PAGE_ALIGN_UP(x)Round up to next page boundary
PAGE_ALIGN_DOWN(x)Round down to page boundary
phys_to_virt(phys)Physical to virtual via HHDM offset
virt_to_phys(virt)Virtual to physical via HHDM offset

Functions

FunctionDescription
pmm_init(memmap, hhdm_offset, kernel_end)Initialize from Limine memory map
pmm_alloc()Allocate one frame (order 0)
pmm_alloc_zeroed()Allocate one zeroed frame (pool or alloc+memset)
pmm_alloc_contiguous(n)Allocate n physically contiguous pages
pmm_free(phys)Return a frame to the allocator
pmm_free_pages()Return count of free frames
pmm_total_pages()Return total managed frames
pmm_usable_pages()Return frames marked usable by Limine

Initialization

  1. Parse Limine memory map to find highest usable address
  2. Compute total frames from usable regions
  3. Allocate LL-Free metadata from the largest usable region (after kernel_end_phys)
  4. Initialize LL-Free with LLFREE_INIT_FREE (all frames free)
  5. Fill the zero-page pool (32 pre-allocated zeroed pages)

Requires at least 512 managed frames.

Zero-Page Pool

The PMM maintains a stack of 32 pre-allocated zeroed pages (g_zpool). pmm_alloc_zeroed() first checks this pool; if empty, falls back to pmm_alloc() + memset(0). This avoids repeated zeroing for common allocations.

LL-Free Integration

The PMM delegates to the LL-Free allocator for all frame management. LL-Free is a three-tier lock-free buddy-like allocator with CPU-local reservations for scalability:

  • Frame: 4 KiB base unit
  • Child: 512 frames = 2 MiB (huge page boundary)
  • Tree: 8 children = 4096 frames = 16 MiB
  • Local: Per-CPU reservation of one tree

Maximum allocatable order is 12 (4096 frames = 16 MiB contiguous).

Last reviewed: 2026-07-22

VMM

This document describes the Virtual Memory Manager (VMM) in the Kyronix kernel. It is the child of Memory Management.

Source

kernel/mm/vmm.c, kernel/mm/vmm.h

Overview

The VMM manages 4-level x86-64 page tables (PML4 -> PDPT -> PD -> PT) for both kernel and user address spaces. It provides page mapping, unmapping, protection changes, and demand paging support.

Address Space Layout

  • User half: 0x000000000000 to 0x800000000000 (128 TiB)
  • Kernel half: 0x800000000000 to 0xFFFFFFFFFFFF (128 TiB)

Kernel page table entries (PML4 indices 256-511) are shared across all address spaces.

Composite Flags

NameValueMeaning
VMM_KCODEPRESENTKernel code: present, NX off
VMM_KDATAPRESENT | WRITE | NXKernel data: present, writable, NX
VMM_UCODEPRESENT | USERUser code: present, user, NX off
VMM_UDATAPRESENT | WRITE | USER | NXUser data: present, writable, user, NX

Functions

FunctionDescription
vmm_init()Enable NX bit via EFER, read kernel PML4 from CR3
vmm_map(sp, virt, phys, flags)Map a single page (allocates intermediate tables as needed)
vmm_unmap(sp, virt)Unmap a single page (zeroes leaf PTE)
vmm_protect(sp, virt, flags)Change flags on existing mapping
vmm_virt_to_phys(sp, virt)Walk page tables for virtual-to-physical translation
vmm_user_range_ok(sp, virt, len, write)Validate user range accessibility
vmm_user_range_fault_in(sp, virt, len, write)Demand page user range (allocates on fault)
vmm_space_new()Create new address space (copies kernel half)
vmm_space_free(sp)Free all user-half page tables and frames
vmm_switch(sp)Switch address space (write CR3)
vmm_fork_user(dst, src)Deep-copy entire user-half address space

Page Table Indexing

LevelIndex MacroBits
PML4(va >> 39) & 0x1FFBits 39-47
PDPT(va >> 30) & 0x1FFBits 30-38
PD(va >> 21) & 0x1FFBits 21-29
PT(va >> 12) & 0x1FFBits 12-20

Demand Paging

vmm_user_range_fault_in() implements demand paging:

  1. Check if the VMA subsystem permits the fault (vma_page_fault_allowed)
  2. Allocate a zeroed physical page via pmm_alloc_zeroed()
  3. Map it into the address space with appropriate flags
  4. This allows lazy allocation of user memory on first access

Address Space Management

  • Maximum 256 concurrent address spaces (VMM_MAX_SPACES)
  • Each space stores its PML4 physical address and a flat array of up to 2048 VMAs
  • vmm_space_new() copies kernel half entries from g_kernel_space
  • vmm_space_free() recursively frees all user-half page table levels

Last reviewed: 2026-07-22

Heap

This document describes the kernel heap allocator in the Kyronix kernel. It is the child of Memory Management.

Source

kernel/mm/heap.c, kernel/mm/heap.h

Overview

The kernel heap provides dynamic memory allocation via kmalloc() and kfree(). It uses a first-fit linked-list allocator with block coalescing, backed by physically contiguous pages from the PMM.

Address Range

ConstantValueDescription
HEAP_START0xffff910000000000Heap virtual base
HEAP_MAX0xffff920000000000Maximum heap (4 GiB)

Data Structure

typedef struct block_hdr {
    uint64_t         size;  // Payload size (excluding header)
    uint64_t         free;  // 1 = free, 0 = allocated
    struct block_hdr *prev;
    struct block_hdr *next;
} block_hdr_t;  // 32 bytes

Allocation Algorithm

kmalloc(size)

  1. Align requested size to 16 bytes
  2. Disable IRQs, acquire spinlock
  3. Walk linked list for first-fit free block
  4. If no block found, call heap_grow() (16 pages = 64 KiB minimum)
  5. If block is large enough to split (remaining >= 48 bytes), split it
  6. Mark block as allocated, update stats

kfree(ptr)

  1. Disable IRQs, acquire spinlock
  2. Mark block free
  3. Forward coalesce: merge with next block if free
  4. Backward coalesce: merge with previous block if free

heap_grow(min_payload)

  1. Compute pages needed: ceil(min_payload / PAGE_SIZE) (minimum 16 pages)
  2. Allocate contiguous pages via pmm_alloc_contiguous()
  3. Map each page into heap range with VMM_KDATA flags
  4. Append new free block to linked list

Functions

FunctionDescription
heap_init()Initial heap growth (64 KiB)
kmalloc(size)First-fit allocation with 16-byte alignment
kcalloc(nmemb, size)Allocation + zero-fill
krealloc(ptr, new_size)Resize (copy if needed)
kfree(ptr)Free with coalescing
heap_stats()Print block count, used/free in KiB
heap_alloc_delta()Net allocated bytes (alloc - free)
heap_walk_used(callback, user)Walk all allocated blocks (for kmemleak)

Thread Safety

The heap uses a spinlock with IRQ save/restore for all operations. This ensures safe concurrent access from multiple CPUs and interrupt handlers.

Last reviewed: 2026-07-22

VMA

This document describes the Virtual Memory Area (VMA) subsystem in the Kyronix kernel. It is the child of Memory Management.

Source

kernel/mm/vma.c, kernel/mm/vma.h

Overview

VMAs track which virtual address ranges are mapped in each address space. They are used for demand paging, mmap/munmap tracking, and page fault handling.

Protection Constants

ConstantValueDescription
PROT_READ0x1Read permission
PROT_WRITE0x2Write permission
PROT_EXEC0x4Execute permission

Data Structure

typedef struct {
    uint64_t start;         // Start virtual address
    uint64_t end;           // End virtual address
    uint32_t prot;          // Protection flags (PROT_READ/WRITE/EXEC)
    uint32_t map_flags;     // VMM mapping flags
    uint8_t  used;          // Slot in use
    uint8_t  free_on_unmap; // Free physical pages on unmap
} vmm_vma_t;

VMAs are stored in a flat array of 2048 entries (VMM_VMA_MAX) inside each vmm_space_t. All lookups are linear scans.

Functions

FunctionDescription
vma_reset(sp)Zero all VMA slots
vma_copy(dst, src)Copy VMA array between address spaces
vma_conflicts(sp, start, len)Check for overlapping VMAs
vma_add(sp, start, len, prot, flags, free_on_unmap)Register a new VMA
vma_remove(sp, start, len)Remove a VMA range (with splitting)
vma_remove_overlaps(sp, start, len)Remove all overlapping VMAs
vma_protect(sp, start, len, prot)Change protection on a range
vma_page_fault_allowed(sp, addr, write, exec)Check if a page fault can be handled
vma_page_flags(sp, addr)Convert VMA protection to PTE flags
vma_range_ok(sp, start, len)Validate range is fully covered by VMAs

VMA Splitting

When removing a sub-range from a VMA, four cases apply:

  1. No leftovers: Clear the VMA entirely
  2. Left only: Shrink v->end = start
  3. Right only: Shrink v->start = end
  4. Split: Create two VMAs (left and right of the hole)

Case 4 requires an empty VMA slot and may fail with -ENOMEM.

Page Fault Integration

vma_page_fault_allowed() is called by the VMM’s demand paging path. It returns true only if:

  • A VMA covers the faulting address
  • The VMA has free_on_unmap set
  • The VMA has sufficient permissions (PROT_READ minimum, PROT_WRITE for writes, PROT_EXEC for instruction fetches)

This enables lazy allocation: pages are allocated on first access rather than at mmap time.

Last reviewed: 2026-07-22

Process Management

This document describes the process management subsystem of the Kyronix kernel. It is the child of Kernel and parent of process management component documents.

Components

ComponentSourceDescription
Process Tableproc/proc.cProcess allocation, lifecycle, scheduling
Schedulerproc/sched.SContext switch (assembly)
SMPproc/smp.cCPU enumeration, AP boot
Signalsproc/signal.cPOSIX signal delivery
Jailsproc/jail.cSandbox/container isolation
ELF Loaderexec/elf.c64-bit ELF parsing and loading
Process Execexec/process.cExec, stack setup, userspace entry

Process States

StateValueDescription
PROC_UNUSED0Slot is free
PROC_RUNNING1Currently executing on a CPU
PROC_READY2Eligible to run, waiting for time slice
PROC_WAITING3Blocked (I/O, signal, etc.)
PROC_ZOMBIE4Exited, not yet reaped
PROC_DYING5In the process of exiting
PROC_STOPPED6Job-control stopped

Process Table

  • Fixed array of 64 slots (PROC_MAX)
  • Spinlock-protected allocation
  • PID = slot index + 1 (PID 0 reserved for idle)

Global Bitmasks

BitmaskDescription
g_ready_maskProcesses in READY state (eligible for scheduling)
g_used_maskAll allocated process slots
g_timer_maskProcesses with active timers

Last reviewed: 2026-07-22

Scheduler

This document describes the scheduler in the Kyronix kernel. It is the child of Process Management.

Source

kernel/proc/proc.c, kernel/proc/sched.S

Algorithm

The scheduler implements per-CPU round-robin with lock-free bitmap scanning. The g_ready_mask 64-bit bitmask enables O(1) next-process selection via __builtin_ctzll (count trailing zeros).

Key Functions

FunctionDescription
proc_next_ready(skip)Find next ready process via bitmap scan
sched_claim_next(skip)CAS-based claim (READY -> RUNNING)
sched_yield_blocking()Voluntary yield on block
sched_switch(next)Context switch (assembly)
proc_idle_until_ready(skip)Busy-wait for ready process
proc_create_idle(cpu_id, entry)Create idle process for a CPU

Context Switch (sched_switch)

The context switch in sched.S performs:

  1. Save callee-saved registers (RBX, RBP, R12-R15)
  2. Save FPU/SSE state via fxsave64
  3. Save FS base via rdmsr(IA32_FS_BASE)
  4. Save kernel stack pointer (kstack_rsp) and user RSP
  5. Load next process’s kernel stack, FS base, user RSP
  6. Switch address space via vmm_switch(next->space) (CR3)
  7. Restore FPU/SSE state via fxrstor64
  8. Pop callee-saved registers and return

SMP Scheduling

Each CPU runs its own scheduling loop (ap_sched_loop):

  1. Try sched_claim_next(idle) – CAS from READY to RUNNING
  2. If found: switch from idle to the claimed process
  3. If not found: sti; hlt (halt until next interrupt)

Preemption occurs on both PIC IRQ 0 and LAPIC timer tick (vector 224). If a higher-priority process is found via sched_claim_next, the current process is preempted.

Kernel Stack Layout

Each process gets 16 pages (64 KiB) of kernel stack plus 1 guard page:

  • Guard page: unmapped (detects overflow)
  • Usable stack: 16 pages mapped with VMM_KDATA
  • Virtual base: 0xffff920000000000 (bump-allocated)

FPU State

Each process has 512 bytes of FPU/SSE state (fpu_state) at offset 3328, saved/restored on every context switch via fxsave64/fxrstor64. Initialized with FCW=0x037F and MXCSR=0x1F80 (all exceptions masked).

Last reviewed: 2026-07-22

SMP

This document describes the Symmetric Multi-Processing (SMP) support in the Kyronix kernel. It is the child of Process Management.

Source

kernel/proc/smp.c, kernel/proc/smp.h, kernel/proc/ap_trampoline.S

Overview

SMP support enables the kernel to run on multiple CPU cores. The BSP (Bootstrap Processor) discovers CPUs via the Limine SMP protocol and wakes APs (Application Processors) via a trampoline mechanism.

CPU Discovery (smp_init)

  1. Read limine_smp_response from bootloader
  2. Set g_cpu_count to reported CPU count
  3. Identify BSP by its LAPIC ID, assign CPU ID 0
  4. Assign sequential IDs to APs (starting at 1)
  5. Store &g_cpu_local[cid] in each CPU’s extra_argument

AP Boot Sequence (smp_boot_aps)

  1. Create idle process for each non-BSP CPU
  2. Write ap_trampoline to each CPU’s goto_address (Limine wake-up mechanism)
  3. Wait until g_aps_ready == g_cpu_count - 1

AP Trampoline (ap_trampoline)

The AP entry point in assembly:

  1. cli – disable interrupts
  2. Load limine_smp_info.extra_argument (points to cpu_local_t)
  3. Load cpu_local_t.current (idle process)
  4. Load proc_t.kstack_top as RSP
  5. Call ap_init_cpu(cpu_local_t *) in C

AP Initialization (ap_init_cpu)

Each AP performs full initialization (in order):

  1. GDT: gdt_ap_load(cpu_id) – per-CPU GDT + TSS
  2. IDT: idt_load_ap() – reload IDT
  3. MSR setup: Write IA32_GS_BASE and IA32_KERNEL_GS_BASE
  4. SSE: cpu_enable_sse()
  5. SYSCALL MSRs: Configure SYSCALL/SYSRET (same as BSP)
  6. LAPIC: Enable spurious vector, mask LVT entries
  7. Signal readiness: Atomically increment g_aps_ready
  8. Wait for BSP: Spin on g_kernel_ready
  9. Start timer: lapic_timer_start_periodic(250)
  10. Enter ap_sched_loop()

Per-CPU Data

Each CPU has a cpu_local_t structure accessed via GS:

OffsetFieldDescription
0kernel_rspKernel stack pointer
8user_rspUser stack pointer
16cpu_idCPU identifier
32currentCurrent process
40idleIdle process

Maximum supported CPUs is defined by MAX_CPUS.

Last reviewed: 2026-07-22

Signals

This document describes the signal handling implementation in the Kyronix kernel. It is the child of Process Management.

Source

kernel/proc/signal.c, kernel/proc/signal.h

Overview

The Kyronix kernel implements POSIX-compatible signal handling with Linux ABI compatibility. Signals are delivered by constructing an rt_sigframe on the user stack and redirecting execution to the signal handler.

Supported Signals

SignalValueDefault Action
SIGHUP1Fatal
SIGINT2Fatal
SIGQUIT3Fatal
SIGILL4Fatal
SIGTRAP5Fatal
SIGABRT6Fatal
SIGBUS7Fatal
SIGFPE8Fatal
SIGKILL9Fatal (uncatchable)
SIGUSR110Fatal
SIGSEGV11Fatal
SIGUSR212Fatal
SIGPIPE13Fatal
SIGALRM14Fatal
SIGTERM15Fatal
SIGCHLD17Non-fatal (ignore)
SIGCONT18Non-fatal (continue)
SIGSTOP19Stop (uncatchable)
SIGTSTP20Stop
SIGTTIN21Stop
SIGTTOU22Stop
SIGWINCH28Non-fatal (ignore)

Signal Delivery

signal_check(f)

Called on every syscall entry/exit and timer interrupt:

  1. Check terminal-driven signals via tty_check_signals()
  2. Load pending signals masked by complement of sig_mask
  3. Find lowest-numbered unmasked signal via __builtin_ctzll
  4. Clear from pending_sigs
  5. If ptrace-traced, call proc_ptrace_stop()
  6. Otherwise call deliver_signal()

deliver_signal(p, sig, frame)

  • SIGSTOP/SIGTSTP (default): Enter job-control stop
  • SIG_IGN: No action
  • SIG_DFL: If fatal, call proc_do_exit(-sig)
  • Custom handler: Build signal frame and redirect execution

setup_sigframe(p, sig, frame)

  1. Check for alternate signal stack (SA_ONSTACK)
  2. Allocate rt_sigframe_t (440 bytes) below user RSP
  3. Populate with register snapshot, signal info, ucontext
  4. Update signal mask (add signal bit + sa_mask)
  5. Redirect: rcx = handler, rdi = signal number, rsi = siginfo, rdx = ucontext

Signal Action Flags

FlagValueEffect
SA_NOCLDSTOP0x0001Don’t send SIGCHLD on stops
SA_NOCLDWAIT0x0002Don’t create zombies
SA_SIGINFO0x0004Extended handler (3-arg)
SA_ONSTACK0x08000000Execute on alternate stack
SA_RESETHAND0x80000000Reset to SIG_DFL after delivery
SA_NODEFER0x40000000Don’t block signal during handler

Alternate Signal Stack

Each process can have an alternate stack configured via sigaltstack(). The frame is placed at the top of the alternate stack when SA_ONSTACK is set.

Last reviewed: 2026-07-22

Jail

This document describes the jail (sandbox) subsystem in the Kyronix kernel. It is the child of Process Management.

Source

kernel/proc/jail.c, kernel/proc/jail.h

Overview

Jails provide process isolation through hierarchical containment with four independent isolation axes: filesystem, PID namespace, IPC, and privilege restriction. Up to 32 jails can exist simultaneously.

Jail Flags

FlagValueEffect
JAILF_FS0x01Filesystem isolation (chroot-like root)
JAILF_PID0x02PID namespace isolation
JAILF_IPC0x04IPC isolation
JAILF_PRIV0x08Privilege restriction (root inside jail is restricted)

Jail States

StateValueDescription
JAIL_UNUSED0Slot is free
JAIL_ACTIVE1Jail is operational
JAIL_DYING2Pending destruction (waiting for processes to exit)

Data Structures

typedef struct {
    int      state;
    uint32_t id;
    uint32_t parent_id;
    uint32_t flags;
    char     name[32];
    char     root[256];
    int      nprocs;
    int      max_procs;
    uint32_t creator_uid;
} jail_t;

Syscalls

SyscallNumberDescription
jail_create500Create a new jail
jail_attach501Move process into a jail
jail_get502Get jail info by ID
jail_list503List all visible jails
jail_remove504Remove/destroy a jail
jail_self505Get current process’s jail ID
jail_set_auto506Toggle auto-isolation mode

Key Functions

FunctionDescription
jail_init()Zero all jail slots
jail_create(parent_id, cfg, uid)Create jail with parent relationship
jail_enter(p, jid)Move process into jail
jail_remove(jid, requester)Remove jail (permission checked)
jail_can_see(observer, target)PID namespace visibility check
jail_host_priv(p)Check host-level privilege
jail_can_fork(jid)Check fork permission and capacity
jail_root_current()Get current process’s jail root
jail_canon_clamp(path, sz, root)Clamp path within jail root

Path Canonicalization

The jail system canonicalizes paths to prevent directory traversal escapes:

  1. path_canon() resolves . and .., collapses multiple slashes
  2. jail_canon_clamp() ensures the result stays within the jail’s root
  3. jail_strip_root() removes the jail root prefix for getcwd() display

Hierarchy

Jails form a tree rooted at JAIL_HOST (0). A process can only enter a jail that is a descendant of its current jail. This prevents privilege escalation by entering an ancestor jail.

Auto-Isolation

When g_jail_auto_isolate is enabled (via jail_set_auto), every execve() call creates a fresh jail for the process. The init process and its direct descendants are exempt (marked with JAILF_EXEMPT).

Last reviewed: 2026-07-22

Filesystems

This document describes the filesystem subsystem of the Kyronix kernel. It is the child of Kernel and parent of filesystem component documents.

Components

ComponentSourceDescription
VFSfs/vfs.cVirtual Filesystem Switch layer
ext2fs/ext2.cext2/3 filesystem driver
FAT32fs/fat32.cFAT32 filesystem driver
procfsfs/procfs.cProcess information filesystem (/proc)
devfsfs/devfs.cDevice filesystem (/dev)
eventfdfs/eventfd.cEvent file descriptor
pipefs/pipe.cAnonymous pipes
CPIOfs/cpio.cCPIO archive loader (initrd)
fstabfs/fstab.c/etc/fstab parser
Unix socketfs/unix_socket.cUnix domain sockets
Inet socketfs/inet_socket.cInternet domain sockets (lwIP)

Mount Points

The kernel mounts the following filesystems at boot:

MountSourceDescription
/procprocfsProcess information
/sysdevfsSystem/device nodes
/dev/ptsdevfsPseudo-terminal devices
/ext2 or initrdRoot filesystem

File Descriptor Operations

The VFS layer provides the following operations on file descriptors:

OperationFunction
Readfd_read()
Writefd_write()
Seekfd_lseek()
Closefd_close()
Statfd_stat() / fd_fstat()
Dupfd_dup() / fd_dup2() / fd_dup3()
Pollfd_pollin() / fd_pollout() / fd_pollhup()
Ioctlfd_ioctl()
Getdentsfd_getdents64()

Last reviewed: 2026-07-22

VFS

This document describes the Virtual Filesystem Switch (VFS) in the Kyronix kernel. It is the child of Filesystems.

Source

kernel/fs/vfs.c, kernel/fs/vfs_internal.h

Overview

The VFS provides a unified interface for all filesystem operations. It manages mount points, file descriptor tables, node reference counting, and filesystem registration.

Data Structures

vfs_node_t

Represents a file, directory, device, or special node:

FieldDescription
typeNode type (VFS_TYPE_REG, VFS_TYPE_DIR, VFS_TYPE_DEV, etc.)
nameNode name
sizeFile size in bytes
modePermission bits
uid, gidOwner and group
dataFilesystem-specific data
fs_opsFilesystem operations (read, write, create, etc.)
refcountReference count

vfs_file_t

Represents an open file descriptor:

FieldDescription
nodeAssociated VFS node
offsetCurrent file position
flagsOpen flags (O_RDONLY, O_WRONLY, etc.)
pipePipe data (for pipe FDs)
inetInet socket data

Mount System

FunctionDescription
vfs_init()Initialize VFS, mount /proc, /sys, /dev/pts
vfs_mount(path, fs, dev)Mount a filesystem at a path
vfs_lookup(path)Look up a node by path
vfs_register_fs(fs)Register a filesystem driver

Path Resolution

Paths are resolved relative to the process’s current working directory (cwd) and jail root. The resolution process:

  1. If path is absolute and jail root exists, prepend jail root
  2. Walk components, resolving . and ..
  3. Return refcounted node pointer

Filesystem Operations

Each registered filesystem provides:

OperationDescription
check_root(bd)Check if a block device contains this filesystem
mount(bd, path)Mount the filesystem
read(node, buf, off, len)Read data
write(node, buf, off, len)Write data
create(node, name, type)Create a file or directory
unlink(node, name)Remove a file
readdir(node, entries)List directory contents

Reference Counting

VFS nodes use reference counting to prevent use-after-free. vfs_lookup() increments the refcount; vfs_node_unref_internal() decrements it. When the refcount reaches zero, the node is freed.

Last reviewed: 2026-07-22

ext2

This document describes the ext2 filesystem driver in the Kyronix kernel. It is the child of Filesystems.

Source

kernel/fs/ext2.c

Overview

The ext2 driver provides read/write access to ext2/3 filesystems. It is the primary disk-based filesystem used for the root partition.

Features

  • Block size: 4096 bytes
  • Inode-based file storage
  • Directory entries with inode references
  • Symbolic link support
  • File creation, deletion, renaming
  • Permission bits (mode, uid, gid)

Mount Process

  1. Read superblock from block device
  2. Validate magic number and block size
  3. Initialize block and inode bitmaps
  4. Mount at specified path

Block Allocation

The driver maintains block and inode bitmaps. Free blocks are found via linear scan of the bitmap. Block groups are used to distribute metadata across the disk.

Registration

ext2_init();  // Register ext2 filesystem driver

After registration, the kernel attempts to mount ext2 on block devices during boot.

Last reviewed: 2026-07-22

FAT32

This document describes the FAT32 filesystem driver in the Kyronix kernel. It is the child of Filesystems.

Source

kernel/fs/fat32.c kernel/fs/fat32.h

Overview

The FAT32 driver provides read/write access to FAT32-formatted disk partitions. The driver supports directory traversal, file read/write, and file creation.

Features

  • Directory traversal
  • File read/write
  • File creation

VFS Integration

The FAT32 driver integrates with the VFS layer through standard filesystem operations.

Last reviewed: 2026-07-22

CPIO

This document describes the CPIO archive loader in the Kyronix kernel. It is the child of Filesystems.

Source

kernel/fs/cpio.c kernel/fs/cpio.h

Overview

The CPIO loader provides read-only access to CPIO archives used as initial ramdisks (initrds). The loader extracts files from the CPIO archive passed by the Limine bootloader as a module.

VFS Integration

The CPIO loader integrates with the VFS layer to present archive contents as a filesystem.

Last reviewed: 2026-07-22

procfs

This document describes the procfs filesystem in the Kyronix kernel. It is the child of Filesystems.

Source

kernel/fs/procfs.c

Overview

procfs is an in-memory filesystem mounted at /proc that exposes process and kernel information. It provides a read-only view of system state.

Mount Point

/proc

Entries

EntryDescription
/proc/<pid>/Per-process information directories
/proc/self/Symlink to current process
/proc/meminfoMemory usage statistics
/proc/versionKernel version string
/proc/uptimeSystem uptime

Implementation

procfs nodes are dynamically generated on lookup. The filesystem does not persist data to disk; all information is gathered from kernel state at access time.

Last reviewed: 2026-07-22

devfs

This document describes the devfs filesystem in the Kyronix kernel. It is the child of Filesystems.

Source

kernel/fs/devfs.c

Overview

devfs is an in-memory filesystem mounted at /dev that provides device nodes. It dynamically creates entries for registered character and block devices.

Mount Points

MountDescription
/devDevice nodes
/sysSystem information
/dev/ptsPseudo-terminal devices

Device Registration

Drivers register device nodes via devfs_register():

DevicePathDescription
Framebuffer/dev/fb0Linear framebuffer device
Input event 0/dev/input/event0Keyboard input
Input event 1/dev/input/event1Mouse input
AHCI disk/dev/ahci0SATA/AHCI block device

Device Types

TypeDescription
VFS_TYPE_DEVCharacter device
VFS_TYPE_BLKBlock device

Last reviewed: 2026-07-22

eventfd

This document describes the event file descriptor in the Kyronix kernel. It is the child of Filesystems.

Source

kernel/fs/eventfd.c

Overview

eventfd provides a lightweight signaling mechanism between processes or threads. It implements a 64-bit counter that can be read and written atomically.

Syscalls

SyscallNumberDescription
eventfd284Create eventfd with flags
eventfd2290Create eventfd with flags (O_CLOEXEC, O_NONBLOCK)

Operations

OperationBehavior
read()Blocks if counter is 0; otherwise returns counter and resets to 0
write()Adds to the counter atomically
poll()Returns readable when counter > 0, writable when counter < MAX

Implementation

eventfd instances are backed by a 64-bit atomic counter stored in the VFS node’s data field. Reads and writes are performed atomically using spinlocks.

Last reviewed: 2026-07-22

Pipe

This document describes the pipe implementation in the Kyronix kernel. It is the child of Filesystems.

Source

kernel/fs/pipe.c, kernel/fs/fdpipe.c

Overview

Pipes provide unidirectional inter-process communication. The Kyronix kernel implements both anonymous pipes (pipe/pipe2) and named pipes (FIFOs).

Syscalls

SyscallNumberDescription
pipe22Create anonymous pipe
pipe2293Create pipe with flags (O_CLOEXEC, O_NONBLOCK)

Data Structure

typedef struct {
    uint8_t *buf;        // Ring buffer
    size_t   size;       // Buffer capacity
    size_t   read_pos;   // Read position
    size_t   write_pos;  // Write position
    size_t   count;      // Bytes available
    // Synchronization primitives for blocking read/write
} pipe_t;

Operations

OperationBehavior
read()Blocks if empty; reads up to requested bytes
write()Blocks if full; writes up to requested bytes
close()Signals EOF to readers/writers

Ancillary Data (SCM_RIGHTS)

Pipes support file descriptor passing via SCM_RIGHTS ancillary data on Unix domain sockets. This enables processes to share file descriptors across address spaces.

Blocking

Read and write operations block the calling process when the pipe is empty (read) or full (write). Blocked processes are moved to PROC_WAITING state and woken when data becomes available.

Last reviewed: 2026-07-22

Drivers

This document describes the device driver subsystem of the Kyronix kernel. It is the child of Kernel and parent of driver component documents.

Components

ComponentSourceDescription
PCIdrivers/pci.cPCI bus enumeration
ACPIdrivers/acpi.cACPI table parsing
AHCIdrivers/ahci.cSATA/AHCI block device driver
VirtIOdrivers/virtio_net.cVirtIO network device driver
Inputdrivers/input.cEvent-based input subsystem
Keyboarddrivers/kbd.cPS/2 keyboard driver
PS/2 Mousedrivers/ps2mouse.cPS/2 mouse driver
Framebufferdrivers/fb.cLinear framebuffer driver
fbdevdrivers/fbdev.cFramebuffer device (/dev/fb0)
TTYdrivers/tty.cTeletype terminal
Virtual TTYdrivers/vt.cVirtual terminal switching
Serialdrivers/serial.cSerial port (COM1)
Blockdrivers/block.cBlock device abstraction
UIOdrivers/uio.cUserspace I/O device

Initialization Order

Drivers are initialized in kmain() after PCI enumeration and ACPI setup:

  1. block_init() – Block device abstraction
  2. ahci_init() – SATA/AHCI controllers
  3. virtnet_init() – VirtIO network
  4. net_init() – Network stack (lwIP)
  5. uio_init() – Userspace I/O
  6. fbdev_init() – Framebuffer device
  7. input_init() – Input event subsystem
  8. vt_init() – Virtual terminal
  9. pit_init() – Timer
  10. ps2mouse_init() – PS/2 mouse

Last reviewed: 2026-07-22

PCI

This document describes the PCI bus enumeration in the Kyronix kernel. It is the child of Drivers.

Source

kernel/drivers/pci.c

Overview

PCI (Peripheral Component Interconnect) enumeration discovers and configures all PCI devices on the bus. The kernel scans the standard I/O configuration space to identify devices.

Configuration Space Access

PCI configuration is accessed via I/O ports:

PortDescription
0xCF8Configuration Address Register
0xCFCConfiguration Data Register

Device Structure

struct pci_device {
    uint8_t  bus, dev, func;
    uint16_t vendor_id, device_id;
    uint8_t  class, subclass;
    uint8_t  prog_if;
    uint8_t  header_type;
    uint32_t bar[6];
    uint16_t command;
};

Enumeration

pci_enumerate() scans all 256 buses, 32 devices, and 8 functions:

  1. For each device/function, read vendor ID
  2. If vendor ID is 0xFFFF, device does not exist – skip
  3. Read class, subclass, BAR registers, and header type
  4. Store in global device table

BAR (Base Address Register) Types

TypeDescription
I/OMemory-mapped I/O port
MemoryMemory-mapped device registers

BARs are used by drivers (AHCI, VirtIO) to access device registers.

Last reviewed: 2026-07-22

ACPI

This document describes the ACPI implementation in the Kyronix kernel. It is the child of Drivers.

Source

kernel/drivers/acpi.c

Overview

ACPI (Advanced Configuration and Power Interface) provides hardware configuration information. The kernel parses the RSDP (Root System Description Pointer) to locate the ACPI tables.

Initialization

acpi_init(rsdp_address);
  1. Read RSDP from the address provided by Limine
  2. Validate RSDP signature (“RSD PTR “)
  3. Locate RSDT/XSDT (Root/Extended System Description Table)
  4. Parse SDT entries for MADT (APIC), FADT (Fixed ACPI), and others

Key Tables

TablePurpose
RSDPRoot pointer to all ACPI tables
RSDT/XSDTRoot/Extended System Description Table
MADTMultiple APIC Description Table (CPU topology)
FADTFixed ACPI Description Table (PM timer, reset port)

Functions

FunctionDescription
acpi_init(addr)Parse ACPI tables from RSDP address
acpi_available()Check if ACPI was successfully initialized

Last reviewed: 2026-07-22

AHCI

This document describes the AHCI (Advanced Host Controller Interface) driver in the Kyronix kernel. It is the child of Drivers.

Source

kernel/drivers/ahci.c

Overview

AHCI provides access to SATA storage devices. The driver initializes AHCI controllers discovered via PCI enumeration and registers block devices for each attached drive.

Initialization

  1. Scan PCI devices for AHCI class (class=0x01, subclass=0x06)
  2. Read ABAR (AHCI Base Address Register) from PCI BAR5
  3. Map AHCI HBA registers into kernel virtual space
  4. Reset the HBA and detect attached ports
  5. For each active port, initialize command list and FIS structures
  6. Register as block device via block_register()

Block Device Interface

AHCI block devices are accessed through the generic block device layer:

struct block_device {
    void (*read)(uint64_t lba, uint32_t count, void *buf);
    void (*write)(uint64_t lba, uint32_t count, const void *buf);
    uint64_t total_sectors;
    // ...
};

Functions

FunctionDescription
ahci_init()Initialize AHCI controller(s)
ahci_ready()Check if AHCI is operational

NOTE: AHCI devices appear as /dev/ahci0 via devfs.

Last reviewed: 2026-07-22

VirtIO

This document describes the VirtIO network driver in the Kyronix kernel. It is the child of Drivers.

Source

kernel/drivers/virtio_net.c

Overview

VirtIO provides paravirtualized network access for QEMU virtual machines. The driver implements the VirtIO 1.0 network device specification.

Initialization

  1. Scan PCI devices for VirtIO vendor ID (0x1AF4)
  2. Negotiate feature bits with the device
  3. Initialize virtqueues (TX and RX)
  4. Register MAC address (6 bytes)
  5. Set link status to up

Virtqueue Layout

QueuePurpose
TX queueTransmit Ethernet frames
RX queueReceive Ethernet frames

Functions

FunctionDescription
virtnet_init()Initialize VirtIO-net device
virtnet_ready()Check if device is operational
virtnet_send(buf, len)Transmit an Ethernet frame
virtnet_recv(buf, len)Receive an Ethernet frame
virtnet_mac()Get MAC address

Network Integration

The VirtIO-net driver is connected to the lwIP network stack via the kyronix netif layer. Raw Ethernet frames are passed between the driver and lwIP without additional encapsulation.

Last reviewed: 2026-07-22

Input

This document describes the input subsystem in the Kyronix kernel. It is the child of Drivers.

Source

kernel/drivers/input.c, kernel/drivers/kbd.c, kernel/drivers/ps2mouse.c

Overview

The input subsystem provides an event-based interface for keyboard and mouse input. Events are delivered to user-space via character devices at /dev/input/event0 (keyboard) and /dev/input/event1 (mouse).

Event Structure

struct input_event {
    uint32_t type;      // Event type
    uint32_t code;      // Key/button code
    int32_t  value;     // Value (press/release/movement)
};

Event Types

TypeDescription
Keyboard eventKey press/release with scancode
Mouse eventRelative X/Y movement and button state

Devices

DevicePathSource
Keyboard/dev/input/event0PS/2 keyboard
Mouse/dev/input/event1PS/2 mouse

Functions

FunctionDescription
input_init()Initialize input subsystem
kbd_init()Initialize PS/2 keyboard
ps2mouse_init()Initialize PS/2 mouse

Terminal Integration

The input subsystem is connected to the TTY layer. Keyboard events drive the virtual terminal, and mouse events are forwarded to user-space via the event devices.

Last reviewed: 2026-07-22

Framebuffer

This document describes the framebuffer driver in the Kyronix kernel. It is the child of Drivers.

Source

kernel/drivers/fb.c, kernel/drivers/fbdev.c

Overview

The framebuffer driver provides access to the linear framebuffer provided by the Limine bootloader. It supports text rendering via a built-in PSF font and exposes a character device at /dev/fb0.

Initialization

  1. Read framebuffer info from Limine (LIMINE_FRAMEBUFFER_REQUEST)
  2. Map framebuffer into kernel virtual space
  3. Clear screen with background color
  4. Register /dev/fb0 character device via fbdev

Framebuffer Properties

FieldDescription
addressLinear framebuffer physical address
width, heightResolution in pixels
pitchBytes per scanline
bppBits per pixel

Text Rendering

The kernel includes a built-in PSF (PC Screen Font) for text rendering. Each character is 8x16 pixels. The framebuffer supports ANSI color codes for colored text output.

Functions

FunctionDescription
fb_init(lfb)Initialize framebuffer from Limine response
fb_clear(color)Clear screen with solid color
fb_putchar(c)Render a character at the current cursor position
fb_set_color(fg, bg)Set foreground and background colors
fb_cursor_blink_tick(ticks)Update cursor blink state

Last reviewed: 2026-07-22

TTY

This document describes the TTY (teletype) subsystem in the Kyronix kernel. It is the child of Drivers.

Source

kernel/drivers/tty.c, kernel/drivers/vt.c

Overview

The TTY subsystem provides terminal I/O with support for multiple virtual terminals. It handles character buffering, line editing, and ANSI escape sequence processing.

Components

ComponentSourceDescription
TTYtty.cTerminal I/O and line discipline
Virtual TTYvt.cVirtual terminal switching

Features

  • Line buffering with echo
  • ANSI escape sequence parsing (colors, cursor movement)
  • Multiple virtual terminals (switched via keyboard)
  • SIGINT (Ctrl+C), SIGQUIT (Ctrl+) generation
  • Cursor blink timer

Functions

FunctionDescription
tty_putchar(c)Output a character to the current terminal
tty_check_signals()Check for terminal-driven signals

Signal Generation

KeySignalDescription
Ctrl+CSIGINTInterrupt
Ctrl+\SIGQUITQuit
Ctrl+ZSIGTSTPTerminal stop

Last reviewed: 2026-07-22

Serial

This document describes the serial port driver in the Kyronix kernel. It is the child of Drivers.

Source

kernel/drivers/serial.c

Overview

The serial driver provides early kernel output via the COM1 serial port (I/O address 0x3F8). It is the first driver initialized during boot and remains available for debug output throughout kernel execution.

COM1 Port Layout

PortOffsetDescription
0x3F8+0Data register (read/write)
0x3F9+1Interrupt Enable Register
0x3FA+2FIFO Control Register
0x3FB+3Line Control Register
0x3FC+4Modem Control Register
0x3FD+5Line Status Register

Initialization

serial_init(COM1);
  1. Disable interrupts (write 0 to IER)
  2. Enable DLAB (set LCR bit 7)
  3. Set baud rate divisor to 1 (115200 baud)
  4. 8 data bits, 1 stop bit, no parity (LCR = 0x03)
  5. Enable FIFO, clear, 14-byte threshold (FCR = 0xC7)
  6. IRQs enabled, RTS/DSR set (MCR = 0x0B)

Output

Serial output is used for:

  • Kernel debug messages (log_info, log_warn)
  • Boot status messages
  • Test framework output (test-run-log)
  • QEMU serial console (-serial stdio)

NOTE: The serial port is initialized before any other driver, making it the most reliable output path for early boot debugging.

Last reviewed: 2026-07-22

Networking

This document describes the networking subsystem of the Kyronix kernel. It is the child of Kernel and parent of networking component documents.

Components

ComponentSourceDescription
Network Stacknet/net.clwIP initialization and polling
lwIP Gluenet/lwip_glue.cKernel memory allocator bridge
Kyronix Netifnet/netif/kyronix_netif.clwIP network interface driver
VirtIO-Netdrivers/virtio_net.cHardware NIC driver

Architecture

User-space (sockets)
    |
    v
Syscall layer (socket.c)
    |
    v
lwIP TCP/IP stack
    |
    v
kyronix_netif (lwIP netif)
    |
    v
virtio-net driver
    |
    v
QEMU virtio-net device

Network Configuration

SettingValue
IP Address10.0.2.15
Subnet Mask255.255.255.0 (/24)
Gateway10.0.2.2
DNS Server10.0.2.3

These are static addresses matching QEMU’s default user-mode networking (SLIRP) configuration.

Socket Types

TypeSourceDescription
Unix domainfs/unix_socket.cLocal IPC sockets
Internetfs/inet_socket.cTCP/UDP over lwIP

Functions

FunctionDescription
net_init()Initialize lwIP and register network interface
net_poll()Drain virtio-net RX queue, process lwIP timeouts
net_receive(frame, len)Feed raw Ethernet frame to lwIP

Last reviewed: 2026-07-22

lwIP

This document describes the lwIP integration in the Kyronix kernel. It is the child of Networking.

Source

kernel/net/lwip/ (lwIP library), kernel/net/lwip_glue.c

Overview

lwIP (Lightweight IP) is an open-source TCP/IP stack designed for embedded systems. Kyronix integrates lwIP with a glue layer that bridges its memory allocation to the kernel heap.

Glue Layer

The lwip_glue.c file maps lwIP’s expected libc functions to kernel implementations:

lwIP ExpectsKernel Provides
malloc(s)kmalloc(s)
free(p)kfree(p)
calloc(n, s)kcalloc(n, s)
sys_now()g_ticks * 10 (ticks to ms)
strtol(s, end, base)Custom decimal parser

Protocols Supported

  • TCP (connection-oriented)
  • UDP (connectionless)
  • ICMP (ping)
  • ARP (Address Resolution Protocol)
  • IPv4

Initialization

net_init();
  1. Check virtnet_ready()
  2. Call lwip_init()
  3. Configure static IP (10.0.2.15/24, gateway 10.0.2.2)
  4. Register kyronix netif with ethernet_input as input function
  5. Set DNS server to 10.0.2.3

Timeout Processing

lwIP timeouts are processed periodically via sys_check_timeouts(). This is called every 256 polls from net_poll().

Last reviewed: 2026-07-22

Network Interface

This document describes the Kyronix network interface driver in the Kyronix kernel. It is the child of Networking.

Source

kernel/net/netif/kyronix_netif.c

Overview

The kyronix netif bridges lwIP to the virtio-net hardware. It implements the lwIP netif interface for sending and receiving raw Ethernet frames.

Netif Configuration

PropertyValue
Name"e0"
MTU1500
Output (ARP+IP)etharp_output
Link outputkyronix_netif_output
FlagsNETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP
MAC addressFrom virtnet_mac()

Functions

FunctionDescription
kyronix_netif_init(nif)Initialize netif (called by netif_add)
kyronix_netif_input(nif, data, len)Feed raw Ethernet frame into lwIP
kyronix_netif_output(nif, p)Send pbuf chain via virtio-net

Receive Path

  1. virtio-net driver calls net_receive(frame, len)
  2. net_receive calls kyronix_netif_input()
  3. kyronix_netif_input allocates a pbuf from PBUF_POOL
  4. Copies frame data into pbuf chain
  5. Calls nif->input() (which is ethernet_input)

Transmit Path

  1. lwIP calls kyronix_netif_output(nif, p)
  2. Gathers pbuf chain into flat buffer (max 1514 bytes)
  3. Calls virtnet_send(buf, total)

Last reviewed: 2026-07-22

Syscalls

This document describes the syscall interface of the Kyronix kernel. It is the child of Kernel and parent of syscall category documents.

Overview

The Kyronix kernel implements a Linux x86_64 ABI-compatible syscall interface. User programs invoke syscalls via the SYSCALL instruction with the syscall number in RAX and arguments in RDI, RSI, RDX, R10, R8, R9.

Syscall Entry

  1. swapgs (switch to kernel GS base)
  2. Save user RSP, load kernel RSP
  3. Push all GPRs into cpu_state_t frame
  4. Call syscall_dispatch(frame) in C
  5. Restore registers, sysretq to user mode

Categories

CategoryDocumentSyscalls
File Operationsfile.mdread, write, open, close, stat, lseek, dup, ioctl, …
Process Controlprocess.mdfork, clone, execve, exit, wait4, …
Memory Managementmemory.mdmmap, munmap, brk, mprotect, mremap, …
Socket Operationssocket.mdsocket, connect, bind, listen, accept, sendto, recvfrom, …
Timerstimer.mdnanosleep, clock_gettime, alarm, setitimer, …
Epollepoll.mdepoll_create1, epoll_ctl, epoll_wait, …
Futexfutex.mdfutex (WAIT, WAKE, REQUEUE)
Ptraceptrace.mdptrace (TRACEME, PEEK, POKE, CONT, SINGLESTEP, …)

Error Handling

Syscalls return errors via RAX. Negative values represent Linux errno constants (e.g., -ENOENT, -EINVAL).

Signal Delivery

After every syscall, signal_check() is called to deliver pending signals. If a signal is delivered, the syscall may be restarted or interrupted based on signal handler configuration.

Ptrace Integration

The syscall dispatcher checks ptrace_syscall_trace on every entry and exit. If set, the process is stopped with SIGTRAP|0x80 for debugger observation.

Last reviewed: 2026-07-22

File Operations

This document describes the file operation system calls (syscalls) in the Kyronix kernel. It is the child of Syscalls.

Syscall Table

  • 0 read - Read data from a file descriptor (fd)

  • 1 write - Write data to a file descriptor

  • 2 open - Open a file by path

  • 3 close - Close a file descriptor

  • 4 stat - Get file status by path

  • 5 fstat - Get file status by fd

  • 6 lstat - Get file status by path (no symlink follow)

  • 7 poll - Wait for events on fds

  • 8 lseek - Set file offset for a fd

  • 16 ioctl - Control device parameters

  • 17 pread64 - Read from fd at offset

  • 18 pwrite64 - Write to fd at offset

  • 19 readv - Read multiple buffers from fd

  • 20 writev - Write multiple buffers to fd

  • 21 access - Check file accessibility by path

  • 22 pipe - Create a pipe

  • 23 select - Monitor multiple fds for events

  • 32 dup - Duplicate a fd

  • 33 dup2 - Duplicate a fd to a specific number

  • 40 sendfile - Transfer data between fds

  • 72 fcntl - File descriptor control operations

  • 76 truncate - Truncate a file to a specified length

  • 77 ftruncate - Truncate a fd to a specified length

  • 78 getdents64 - Read directory entries

  • 79 getcwd - Get current working directory

  • 80 chdir - Change current working directory

  • 81 fchdir - Change current working directory by fd

  • 82 rename - Rename a file or directory

  • 83 mkdir - Create a directory

  • 84 rmdir - Remove a directory

  • 85 creat - Create a file

  • 86 link - Create a hard link

  • 87 unlink - Remove a file

  • 88 symlink - Create a symbolic link

  • 89 readlink - Read a symbolic link

  • 90 chmod - Change file permissions by path

  • 91 fchmod - Change file permissions by fd

  • 92 chown - Change file owner by path

  • 93 fchown - Change file owner by fd

  • 94 lchown - Change file owner (no symlink follow)

  • 95 umask - Set file creation mask

  • 137 statfs - Get filesystem statistics by path

  • 138 fstatfs - Get filesystem statistics by fd

  • 257 openat - Open file relative to directory fd

  • 258 mkdirat - Create directory relative to directory fd

  • 260 mknodat - Create device node relative to directory fd

  • 261 fchownat - Change file owner relative to directory fd

  • 262 newfstatat - Get file status relative to directory fd

  • 263 unlinkat - Remove file relative to directory fd

  • 264 renameat - Rename file relative to directory fds

  • 265 linkat - Create hard link relative to directory fds

  • 266 symlinkat - Create symbolic link relative to directory fd

  • 267 readlinkat - Read symbolic link relative to directory fd

  • 268 fchmodat - Change file permissions relative to directory fd

  • 269 faccessat - Check file accessibility relative to directory fd

  • 292 dup3 - Duplicate a fd with flags

  • 293 pipe2 - Create a pipe with flags

  • 295 preadv - Read from fd at offset with scatter-gather

  • 296 pwritev - Write to fd at offset with scatter-gather

  • 326 copy_file_range - Copy data between two fds

  • 332 statx - Get extended file status

  • 334 close_range - Close a range of fds

Path Resolution

All path-based syscalls route through path_abs() to resolve user-provided paths to absolute paths. This function applies jail root confinement via jail_root_current() and prepends the current working directory (cwd) for relative paths.

The at-relative syscalls (openat, mkdirat, unlinkat, etc.) use at_resolve() for directory fd resolution. This function validates the directory fd and combines it with the relative path.

Internal Helpers

The kernel provides user pointer validation helpers:

  • uptr_ok(ptr, size) - Validates a user pointer for read access with automatic page faulting
  • uptr_ok_w(ptr, size) - Validates a user pointer for write access with automatic page faulting

These helpers ensure user pointers are in valid user address space before dereferencing.

Last reviewed: 2026-07-22

Process Control

This document describes the process control system calls (syscalls) in the Kyronix kernel. It is the child of Syscalls.

Syscall Table

  • 56 clone - Create a new process/thread

  • 57 fork - Create a child process

  • 58 vfork - Create a child process (treated as fork)

  • 59 execve - Execute a program

  • 60 exit - Terminate a process

  • 61 wait4 - Wait for a process to change state

  • 231 exit_group - Terminate all threads in a process

  • 247 waitid - Wait for a specific process state change

  • 39 getpid - Get process id (PID)

  • 110 getppid - Get parent PID

  • 186 gettid - Get thread id (TID)

  • 102 getuid - Get real user id (UID)

  • 104 getgid - Get real group id (GID)

  • 107 geteuid - Get effective UID

  • 108 getegid - Get effective GID

  • 105 setuid - Set real UID

  • 106 setgid - Set real GID

  • 109 setpgid - Set process group id (PGID)

  • 112 setsid - Create a session and set session id (SID)

  • 113 setreuid - Set real and effective UID

  • 114 setregid - Set real and effective GID

  • 115 getgroups - Get supplementary group list

  • 116 setgroups - Set supplementary group list

  • 117 setresuid - Set real, effective, and saved UID

  • 118 getresuid - Get real, effective, and saved UID

  • 119 setresgid - Set real, effective, and saved GID

  • 120 getresgid - Get real, effective, and saved GID

  • 121 getpgid - Get PGID

  • 122 setfsuid - Set filesystem UID

  • 123 setfsgid - Set filesystem GID

  • 124 getsid - Get session ID

  • 158 arch_prctl - Architecture-specific process register (ARCH_SET_FS, ARCH_SET_GS, ARCH_GET_FS, ARCH_GET_GS)

clone Flags

The clone syscall accepts flags to control process/thread creation behavior:

  • CLONE_VM - Share memory with parent process
  • CLONE_FILES - Share file descriptor table with parent
  • CLONE_THREAD - Create a thread (shared address space)
  • CLONE_SETTLS - Set thread-local storage (TLS)
  • CLONE_PARENT_SETTID - Store child TID at parent-provided address
  • CLONE_CHILD_CLEARTID - Clear child TID at exit and wake waiters
  • CLONE_CHILD_SETTID - Store child TID at child-provided address

execve Details

The execve syscall performs the following operations:

  1. Reads the shebang line (#!) if present and interprets the interpreter path
  2. Loads the executable binary (Executable and Linkable Format (ELF))
  3. For dynamically linked executables, sets up the dynamic linker via PT_INTERP segment at virtual address 0x7f0000000000
  4. Sets Position Independent Executable (PIE) base address at 0x400000
  5. Sets up the user stack with argc, argv, envp, and auxiliary vector (auxv)
  6. Initializes Address Space Layout Randomization (ASLR) for the memory map bump allocator (mmap_bump)

fork Details

The fork syscall performs the following operations:

  1. Deep-copies the address space via vmm_fork_user()
  2. Copies the file descriptor table
  3. Creates a new kernel stack for the child process
  4. The child process returns with rax set to 0

clone Thread Support

The clone syscall with CLONE_THREAD flag creates a thread that shares:

  • The address space (via CLONE_VM)
  • The file descriptor table (via CLONE_FILES)

wait4 Details

The wait4 syscall supports the following features:

  • Zombie reaping of terminated children
  • Handling of ptrace-stopped tracees
  • Job-stopped children
  • Non-blocking operation via WNOHANG flag

proc_do_exit

The proc_do_exit function performs the following cleanup operations:

  1. Cleans up System V Shared Memory (SHM)
  2. Unreferences the jail
  3. Releases the file descriptor table
  4. Reparents children to PID 1 (init process)
  5. Delivers SIGCHLD signal to the parent process

Last reviewed: 2026-07-22

Memory Management Syscalls

This document describes the memory management system calls (syscalls) in the Kyronix kernel. It is the child of Syscalls.

Syscall Table

  • 9 mmap - Map memory or files into address space

  • 10 mprotect - Set memory protection on a region

  • 11 munmap - Unmap memory from address space

  • 12 brk - Change data segment size

  • 25 mremap - Remap a virtual memory area (VMA)

  • 26 msync - Synchronize memory with storage (noop for ramfs)

  • 27 mincore - Determine page residency in memory

  • 28 madvise - Provide advice about memory usage (noop)

  • 29 shmget - Get System V shared memory segment

  • 30 shmat - Attach shared memory to address space

  • 31 shmctl - Control shared memory operations

  • 67 shmdt - Detach shared memory from address space

mmap Flags

The mmap syscall accepts the following flags:

  • MAP_ANON - Create an anonymous mapping (not backed by a file)
  • MAP_FIXED - Place the mapping at the exact address specified
  • MAP_PRIVATE - Create a private copy-on-write (COW) mapping
  • MAP_SHARED - Create a shared mapping visible to other processes

mmap Details

The mmap syscall uses mmap_pick_addr() to select a virtual address for anonymous mappings. The address selection uses a bump allocator that starts at:

p->mmap_bump = 0x0000500000000000 + random_offset

The mmap implementation supports:

  • Anonymous mappings (file descriptor is -1)
  • File-backed mappings (valid file descriptor)
  • Character device custom mappings (User I/O (UIO))

brk Details

The brk syscall implements the classic break mechanism for heap management:

  • Operates with page-granularity (minimum allocation is one page)
  • Allocates zeroed pages via pmm_alloc_zeroed()
  • Tracks pages_alloc and pages_freed counters
  • Expands the data segment by mapping new pages
  • Contracts the data segment by unmapping pages

mprotect Details

The mprotect syscall changes both:

  • Virtual Memory Area (VMA) metadata (protection flags)
  • Page table entries (hardware-level permissions)

Shared Memory (SHM)

The SHM syscalls implement System V shared memory with the following characteristics:

  • Requires JAILF_IPC flag for jail isolation
  • Maximum of 64 shared memory segments per process
  • Maximum of 4096 pages (16 MiB at 4 KiB pages) per segment
  • Supports attach, detach, and control operations

Last reviewed: 2026-07-22

Socket Operations

This document describes the socket operation syscalls in the Kyronix kernel. It is the child of Syscalls.

Syscall Table

  • 41 socket - Create a socket endpoint
  • 42 connect - Initiate a connection on a socket
  • 43 accept - Accept a connection on a socket
  • 44 sendto - Send a message on a socket
  • 45 recvfrom - Receive a message from a socket
  • 46 sendmsg - Send a message with ancillary data
  • 47 recvmsg - Receive a message with ancillary data
  • 48 shutdown - Shut down part of a full-duplex connection
  • 49 bind - Bind a socket to an address
  • 50 listen - Listen for connections on a socket
  • 51 getsockname - Get local address of a socket
  • 52 getpeername - Get remote address of a socket
  • 53 socketpair - Create a pair of connected sockets
  • 54 setsockopt - Set a socket option
  • 55 getsockopt - Get a socket option
  • 288 accept4 - Accept a connection with flags

Socket Types

The socket syscall routes to one of two backends based on the domain argument.

AF_UNIX (Unix Domain Sockets)

Unix domain sockets are implemented in unix_socket.c. The domain value is 1. Only SOCK_STREAM is supported. Unix domain sockets provide local inter-process communication (IPC) using pipe-backed data transfer.

The socket lifecycle follows these states:

  1. SOCK_UNBOUND - Socket created but not yet bound to a path
  2. SOCK_BOUND - Socket bound to a filesystem path or abstract name
  3. SOCK_LISTENING - Socket in listening state, accepting connections

Abstract sockets use names prefixed with a null byte. The kernel maintains a table of MAX_ABSTRACT_SOCKS (16) entries for abstract socket names. Abstract sockets are invisible across jails when the JAILF_IPC flag is set on the originating jail.

AF_INET (Internet Sockets)

Internet sockets are implemented in inet_socket.c using the lwIP (Lightweight IP) stack. The domain value is 2. Three socket types are supported:

  • SOCK_STREAM (1) - Transmission Control Protocol (TCP) connections backed by tcp_pcb
  • SOCK_DGRAM (2) - User Datagram Protocol (UDP) connections backed by upcb
  • SOCK_RAW (3) - Raw IP connections backed by raw_pcb

Each internet socket contains a net_conn_t structure with protocol control blocks, a 16 KiB receive ring buffer for TCP, and an 8-slot datagram queue for UDP/raw sockets.

SCM_RIGHTS (File Descriptor Passing)

The SCM_RIGHTS ancillary data type enables file descriptor passing between processes over Unix domain sockets. The constant value is 1.

  1. The sender calls sendmsg with a control message containing SOL_SOCKET, SCM_RIGHTS, and an array of file descriptors
  2. The kernel extracts VFS (Virtual File System) node pointers from the sender’s file descriptors via fd_get_node()
  3. Nodes are queued into the pipe’s ancillary data ring via pipe_anc_send()
  4. The receiver calls recvmsg and the kernel reconstructs file descriptors from the queued VFS nodes via fd_open_node()
  5. A maximum of PIPE_ANC_MAXFDS file descriptors are transferred per message

SCM_CREDENTIALS (Peer Credential Passing)

The SCM_CREDENTIALS ancillary data type enables peer credential passing. The constant value is 2. The SO_PASSCRED socket option (value 16) must be enabled on the receiving socket to receive credentials.

  1. The receiver enables credential passing via setsockopt(SOL_SOCKET, SO_PASSCRED, &on)
  2. The receiver calls recvmsg with a control buffer large enough to hold a ucred_s structure
  3. The kernel fills the control message with the sender’s process ID (PID), user ID (UID), and group ID (GID)
  4. The ucred_s structure contains pid (int32), uid (uint32), and gid (uint32)

Socket Options

Socket options are managed via setsockopt (syscall 54) and getsockopt (syscall 55) at the SOL_SOCKET (level 1) layer.

Supported Options

OptionValueDirectionDescription
SO_TYPE3GetReturns socket type (1=stream, 2=dgram, 3=raw)
SO_ERROR4GetReturns last error code (always 0)
SO_PASSCRED16SetEnables SCM_CREDENTIALS delivery
SO_PEERCRED17GetReturns peer PID/UID/GID in ucred_s
SO_DOMAIN39GetReturns address family (1=AF_UNIX, 2=AF_INET)

sendmsg / recvmsg (Vectored I/O)

The sendmsg (syscall 46) and recvmsg (syscall 47) syscalls support vectored I/O through scatter-gather lists of iovec structures.

The msghdr layout in memory:

OffsetFieldType
0msg_namevoid pointer
8msg_namelenuint32
12msg_ioviovec pointer
16msg_iovlenint
24msg_controlvoid pointer
32msg_controllenuint64
40msg_flagsuint32

sendmsg

  1. Validate the msghdr pointer and iovec array
  2. For each iovec entry, call fd_write() with the iovec base and length
  3. If a control buffer is present, parse cmsg_len, cmsg_level, and cmsg_type
  4. For SCM_RIGHTS messages, extract file descriptors and pass their VFS nodes through the pipe’s ancillary ring

recvmsg

  1. For internet sockets, delegate to inet_recvfrom() for the first iovec entry and fill msg_name with the source address
  2. For Unix domain sockets, read data from iovec entries via fd_read() or fd_peek() (when MSG_PEEK is set)
  3. If ancillary data is available in the pipe’s receive ring, construct an SCM_RIGHTS control message with reconstructed file descriptors
  4. If SO_PASSCRED is enabled and credentials are available, construct an SCM_CREDENTIALS control message with peer PID/UID/GID

Last reviewed: 2026-07-22

Timers

This document describes the timer syscalls in the Kyronix kernel. It is the child of Syscalls.

Syscall Table

  • 35 nanosleep - Pause execution for a relative time interval
  • 36 getitimer - Get current value of an interval timer
  • 37 alarm - Set a real-time signal alarm
  • 38 setitimer - Set an interval timer
  • 96 gettimeofday - Get current wall-clock time
  • 97 getrlimit - Get resource limits
  • 98 getrusage - Get resource usage (stub, returns zeroed struct)
  • 99 sysinfo - Get system information
  • 100 times - Get process times
  • 201 time - Get time in seconds
  • 228 clock_gettime - Get clock time
  • 229 clock_getres - Get clock resolution
  • 230 clock_nanosleep - Sleep until an absolute or relative clock time
  • 302 prlimit64 - Get or set resource limits

Time Model

The Kyronix kernel derives time from two global counters maintained by the Programmable Interval Timer (PIT) and the Real-Time Clock (RTC):

  • g_ticks (volatile uint64) - Milliseconds since boot, incremented on each PIT tick at approximately 250 Hz
  • g_epoch_base (uint64) - Unix timestamp in seconds at boot time, read from the RTC during initialization

Wall time in milliseconds is computed as:

wall_ms = g_epoch_base * 1000 + g_ticks

The time (syscall 201) and gettimeofday (syscall 96) syscalls both return wall time derived from this formula. The clock_gettime (syscall 228) syscall also uses this computation and returns the result as a timespec structure with seconds and nanoseconds.

Clock Resolution

The clock_getres (syscall 229) syscall reports a resolution of {0, 1000000} nanoseconds (1 millisecond). This reflects the PIT tick rate of approximately 250 Hz, yielding a 4-millisecond tick period. The kernel floors sub-millisecond precision to 1 millisecond in all time calculations.

nanosleep

The nanosleep (syscall 35) syscall pauses the calling process for a specified duration:

  1. Read the requested time from the user-provided timespec structure (seconds and nanoseconds)
  2. Convert the duration to milliseconds: ms = sec * 1000 + nsec / 1000000
  3. Set p->wakeup_tick = g_ticks + ms
  4. Call proc_set_timer(p) to register the timer
  5. Yield the processor in a blocking loop until g_ticks >= deadline or the process is woken by a signal
  6. Clear p->wakeup_tick and return 0

clock_nanosleep

The clock_nanosleep (syscall 230) syscall extends nanosleep with clock ID and flags support:

  1. Read the requested time from the user-provided timespec structure
  2. If the TIMER_ABSTIME flag (bit 0) is set, compute the relative sleep duration as target_ms - current_wall_ms
  3. Otherwise, treat the request as a relative sleep duration
  4. Follow the same blocking loop as nanosleep

alarm

The alarm (syscall 37) syscall sets a real-time signal alarm:

  1. Compute the previous alarm’s remaining seconds from p->alarm_tick
  2. If seconds > 0, set p->alarm_tick = g_ticks + seconds * 1000 and register the timer
  3. If seconds == 0, clear p->alarm_tick
  4. Return the number of seconds remaining on the previous alarm

The alarm delivers SIGALRM to the process when the deadline expires. The kernel checks p->alarm_tick in the PIT interrupt handler (IDT vector 32).

itimer (Interval Timer)

The setitimer (syscall 38) and getitimer (syscall 36) syscalls manage interval timers that deliver SIGALRM at periodic intervals:

  1. setitimer reads the new interval from a itimerval structure containing interval (seconds, microseconds) and value (seconds, microseconds)
  2. The interval is stored in p->itimer_interval_ms and the next trigger in p->itimer_next_tick
  3. getitimer returns the current interval and remaining time until the next trigger
  4. The kernel checks p->itimer_next_tick in the PIT interrupt handler and delivers SIGALRM when the deadline expires

getrlimit / prlimit64

The getrlimit (syscall 97) and prlimit64 (syscall 302) syscalls report resource limits:

ResourceHard LimitSoft Limit
RLIMIT_NOFILE (7)VFS_FD_MAX (1024)VFS_FD_MAX (1024)
All others1 GiB (1073741824)1 GiB (1073741824)

The prlimit64 syscall accepts a PID argument but ignores it, always operating on the current process. The new limits argument (nl) is accepted but not applied.

sysinfo

The sysinfo (syscall 99) syscall returns system information in a fixed structure:

FieldValue
uptimeg_ticks / 1000 (seconds since boot)
totalram256 MiB (268435456 bytes)
freeram128 MiB (134217728 bytes)
mem_unit1 (byte)
procs1

All other fields are zeroed.

times

The times (syscall 100) syscall returns a monotonic tick count (g_ticks + 1). The user-provided tms structure is zeroed. The return value is always positive.

Last reviewed: 2026-07-22

Epoll

This document describes the epoll event polling interface in the Kyronix kernel. It is the child of Syscalls.

Syscall Table

  • 213 epoll_create - Create an epoll instance (delegates to epoll_create1(0))
  • 232 epoll_wait - Wait for events on an epoll instance
  • 233 epoll_ctl - Control an epoll instance
  • 281 epoll_pwait - Wait for events with signal mask (delegates to epoll_wait)
  • 291 epoll_create1 - Create an epoll instance with flags

Operations

The epoll_ctl (syscall 233) syscall modifies the set of file descriptors monitored by an epoll instance:

  • EPOLL_CTL_ADD (1) - Add a file descriptor to the epoll interest list. Returns EEXIST if the file descriptor is already monitored. Returns ENOMEM if the watch limit is reached.
  • EPOLL_CTL_DEL (2) - Remove a file descriptor from the epoll interest list. Returns ENOENT if the file descriptor is not found.
  • EPOLL_CTL_MOD (3) - Modify the events and data associated with an existing file descriptor. Returns ENOENT if the file descriptor is not found.

Events

EventValueDescription
EPOLLIN0x001Data is available for read
EPOLLOUT0x004Ready for write
EPOLLERR0x008Error condition
EPOLLHUP0x010Hang up
EPOLLONESHOT0x40000000One-shot: disarmed after first event delivery

Events EPOLLERR and EPOLLHUP are reported regardless of whether they are in the interest set. The EPOLLONESHOT flag disables the watch after a single event; the watch must be re-armed via EPOLL_CTL_MOD.

Implementation

Epoll state is managed by the g_epolls array with EPOLL_SLOTS (64) entries. Each epoll instance contains:

  • epfd - The file descriptor handle (backed by an internal /dev/null file descriptor)
  • owner_space - The VMM (Virtual Memory Manager) address space that owns the instance
  • w[EPOLL_MAXW] - Array of EPOLL_MAXW (256) watch entries, each containing a file descriptor, events mask, and user data
  • nw - Current number of active watches

Epoll instances are associated with the creating process’s address space. An instance is only findable by processes sharing the same address space. Stale instances (where the backing file descriptor has been closed) are cleaned up during epoll_create1.

Polling

The epoll_wait (syscall 232) and epoll_pwait (syscall 281) syscalls poll all watches in the epoll interest list:

  1. For each watch, check fd_valid() to determine if the file descriptor is still open. If not, report EPOLLERR | EPOLLHUP.
  2. If the watch requests EPOLLIN, check fd_pollin() for available read data.
  3. If the watch requests EPOLLOUT, check fd_pollout() for write readiness.
  4. Check fd_pollhup() unconditionally for hang-up conditions.
  5. If EPOLLONESHOT is set on a triggered watch, disarm the EPOLLIN and EPOLLOUT bits.

The polling loop runs at 5-millisecond intervals (p->wakeup_tick = g_ticks + 5). If events are found or the timeout expires, the syscall returns immediately. If a signal is pending, the syscall returns EINTR.

The timeout parameter controls the maximum wait duration:

  • timeout == 0 - Non-blocking poll, return immediately
  • timeout > 0 - Block for up to timeout milliseconds
  • timeout < 0 - Block indefinitely until events are found or a signal arrives

The epoll_create (syscall 213) syscall delegates to epoll_create1(0). The epoll_pwait (syscall 281) syscall delegates to epoll_wait without applying a signal mask.

Last reviewed: 2026-07-22

Futex

This document describes the futex (Fast Userspace Mutex) implementation in the Kyronix kernel. It is the child of Syscalls.

Syscall Table

  • 202 futex - Fast Userspace Mutex operations

Operations

The futex syscall supports four operation commands, selected by the op argument. The FUTEX_PRIVATE_FLAG (128) and FUTEX_CLOCK_REALTIME (256) flags may be OR’d with the operation but are currently ignored.

FUTEX_WAIT (0)

Atomically checks that the value at uaddr equals val, then blocks the calling process until woken:

  1. Verify *uaddr == val. If not, return EAGAIN.
  2. Find a free slot in g_futex_tab. If no slot is available, return ENOMEM.
  3. Register the current process and uaddr in the slot.
  4. If a timeout is provided (a timespec pointer), compute the deadline in milliseconds: deadline = g_ticks + (sec * 1000 + nsec / 1000000).
  5. Block in a loop until the process is woken, the deadline expires, or a signal is delivered.
  6. On timeout, return ETIMEDOUT. Otherwise, return 0.

FUTEX_WAKE (1)

Wakes up to val processes waiting on uaddr:

  1. Scan g_futex_tab for entries matching uaddr.
  2. For each matching entry, verify the waiting process is in the same jail as the caller (g_futex_tab[i].proc->jail_id == self->jail_id).
  3. Transition the waiting process from PROC_WAITING to PROC_READY via proc_set_ready().
  4. Return the count of processes woken.

FUTEX_REQUEUE (3)

Moves waiters from uaddr to uaddr2:

  1. Wake up to val waiters on uaddr.
  2. Requeue up to the second argument (passed in the timeout slot) waiters from uaddr to uaddr2 by updating their uaddr field.
  3. Cross-jail checks apply: only processes in the same jail as the caller are affected.

FUTEX_CMP_REQUEUE (4)

Same as FUTEX_REQUEUE, but first verifies that *uaddr == val3. Returns EAGAIN if the comparison fails.

Data Structure

Futex state is stored in the global g_futex_tab array with FUTEX_MAX_WAITERS (PROC_MAX) entries. Each entry contains:

  • uaddr - The userspace address being waited on
  • proc - Pointer to the waiting process (NULL if the slot is free)

The table is protected by g_futex_lock, a spinlock acquired on all read and write operations. Waiter registration and wakeup use __sync_bool_compare_and_swap for atomic state transitions.

CLONE_CHILD_CLEARTID Integration

When a thread created with CLONE_CHILD_CLEARTID (flag 0x00200000) exits:

  1. The kernel writes zero to the cleartid_addr stored in the thread’s process structure.
  2. The kernel calls cleartid_wake(cleartid_addr).
  3. cleartid_wake scans g_futex_tab for all entries whose uaddr matches cleartid_addr.
  4. Each matching process is transitioned from PROC_WAITING to PROC_READY.

This mechanism enables pthread library implementations to use CLONE_CHILD_CLEARTID with a futex address, ensuring that joiners blocked on futex(CLEARTID) are woken when the thread exits.

Last reviewed: 2026-07-22

Ptrace

This document describes the process tracing (ptrace) interface in the Kyronix kernel. It is the child of Syscalls.

Syscall Table

  • 101 ptrace - Process tracing and debugging

Operations

The ptrace syscall supports the following request codes:

PTRACE_TRACEME (0)

Sets the calling process’s tracer_pid to its parent PID. The process becomes traceable by its parent. No arguments are used.

PTRACE_ATTACH (16)

Attaches the calling process as a tracer to a target process:

  1. Look up the target process by PID via proc_find().
  2. Verify the target is not already being traced (tracer_pid == 0). If already traced, return EPERM.
  3. Set t->tracer_pid = self->pid.
  4. Send SIGSTOP to the target process.
  5. Return 0.

PTRACE_PEEKTEXT / PTRACE_PEEKDATA (1, 2)

Reads 8 bytes from the target process’s address space at address addr:

  1. Switch to the target’s address space via vmm_switch().
  2. Validate the user pointer with uptr_ok().
  3. Copy 8 bytes from the target address to a kernel buffer.
  4. Switch back to the caller’s address space.
  5. Write the result to the data pointer in the caller’s address space.

Both request codes behave identically.

PTRACE_POKETEXT / PTRACE_POKEDATA (4, 5)

Writes 8 bytes to the target process’s address space at address addr:

  1. Switch to the target’s address space via vmm_switch().
  2. Validate the user pointer with uptr_ok_w().
  3. Copy 8 bytes from the data argument to the target address.
  4. Switch back to the caller’s address space.

Both request codes behave identically.

PTRACE_GETREGS (12)

Reads the full register state of the target process into a ptrace_user_regs structure:

  1. Call ptrace_fill_regs() to populate the structure from the target’s current frame.
  2. Copy the structure to the caller’s data pointer.

PTRACE_SETREGS (13)

Writes a ptrace_user_regs structure to the target process’s register state:

  1. Copy the ptrace_user_regs structure from the caller’s data pointer.
  2. Call ptrace_store_regs() to apply the register values to the target’s current frame.

PTRACE_CONT (7)

Resumes the target process’s execution without syscall tracing or single-stepping:

  1. Verify the target is in a stopped state (ptrace_stopped != 0). Otherwise, return ESRCH.
  2. If a signal number is provided in data, inject it as a pending signal.
  3. Clear ptrace_stopped and ptrace_reported.
  4. Transition the target from PROC_WAITING to PROC_READY.

PTRACE_SYSCALL (24)

Resumes the target process’s execution with syscall tracing enabled:

  1. Same as PTRACE_CONT, but sets ptrace_syscall_trace = 1.
  2. The syscall dispatcher checks ptrace_syscall_trace on entry and exit, stopping the process with SIGTRAP|0x80 at each syscall boundary.

PTRACE_SINGLESTEP (9)

Resumes the target process’s execution with single-stepping enabled:

  1. Same as PTRACE_CONT, but sets ptrace_step = 1.
  2. The target executes one instruction before being stopped again.

PTRACE_KILL (8)

Injects SIGKILL into the target process:

  1. Set the SIGKILL bit in the target’s pending_sigs.
  2. Clear ptrace_stopped and ptrace_reported.
  3. Transition the target from PROC_WAITING to PROC_READY.

PTRACE_DETACH (17)

Detaches the tracer from the target process:

  1. Clear tracer_pid and ptrace_syscall_trace.
  2. If the target is stopped, clear ptrace_stopped and transition it to PROC_READY.

PTRACE_SETOPTIONS (0x4200)

Accepted as a no-op. Returns 0.

Register State

The ptrace_user_regs structure contains the full x86_64 general-purpose register state:

r15, r14, r13, r12, rbp, rbx, r11, r10, r9, r8,
rax, rcx, rdx, rsi, rdi, orig_rax, rip, cs, eflags,
rsp, ss, fs_base, gs_base, ds, es, fs, gs

Segment registers are set to kernel constants: cs = GDT_USER_CODE_SEL, ss/ds/es/fs/gs = GDT_USER_DATA_SEL. The fs_base is read from the target process’s fs_base field.

Frame Kinds

The ptrace_frame_kind field determines how register values are extracted and stored:

  • Frame kind 1 (syscall_frame_t) - Syscall entry frame. RIP is derived from rcx (the syscall return address). RFLAGS is derived from r11. This frame is used when the process is stopped at a syscall entry/exit via SIGTRAP|0x80.
  • Frame kind 2 (cpu_state_t) - Full interrupt frame. RIP and RFLAGS are read directly from the interrupt frame. This frame is used when the process is stopped via #BP (breakpoint) or #DB (debug) exceptions.

Permission Model

Only the tracer process (identified by t->tracer_pid == self->pid) may operate on a tracee. All operations except PTRACE_TRACEME and PTRACE_ATTACH verify the caller is the tracer. If the caller is not the tracer, the syscall returns ESRCH.

Exception Integration

The kernel’s Interrupt Descriptor Table (IDT) checks tracer_pid on #BP (vector 3) and #DB (vector 1) exceptions:

  1. On #BP, the instruction pointer is decremented by 1 (past the int3 opcode).
  2. The process’s ptrace_orig_rax is saved.
  3. proc_ptrace_stop() is called with frame kind 2, delivering SIGTRAP to stop the process.

Syscall Dispatcher Integration

The syscall dispatcher checks ptrace_syscall_trace on every syscall entry and exit:

  1. On entry, if ptrace_syscall_trace is set and the syscall number is not 101 (ptrace itself), the process is stopped with SIGTRAP|0x80 and frame kind 1.
  2. On exit, after storing the return value, the process is stopped again with SIGTRAP|0x80 and frame kind 1.
  3. The ptrace_in_syscall flag is set on entry and cleared on exit to distinguish entry stops from exit stops.

Last reviewed: 2026-07-22

Implementation Notes

This document provides implementation notes for the Kyronix kernel subsystems. It is the root of the Implementation Notes section.

The Implementation Notes section covers detailed internal implementation details for the kernel and drivers, including initialization sequences, scheduling internals, Interrupt Request (IRQ) handling, memory management algorithms, and Peripheral Component Interconnect (PCI) enumeration.

Sections

  • Kernel — Kernel core subsystem implementation notes
  • Drivers — Driver subsystem implementation notes

Last reviewed: 2026-07-22

Kernel Implementation Notes

This document provides implementation notes for the Kyronix kernel core subsystems. It is the child of Implementation Notes.

The kernel implementation notes detail the internal design and algorithmic choices for initialization, scheduling, interrupt handling, and memory management within the Kyronix kernel core.

Subsections

Last reviewed: 2026-07-22

Kernel Initialization

This document describes the detailed kernel initialization sequence in the Kyronix kernel. It is the child of Kernel Implementation Notes.

The kmain() function executes a nine-phase initialization sequence that brings up all kernel subsystems from early hardware detection through to the first userspace process execution.

Initialization Phases

  1. Phase 1 — Early console and core tables: serial_init, printf setup, Global Descriptor Table (GDT) initialization (gdt_init), Interrupt Descriptor Table (IDT) initialization (idt_init), and keyboard initialization (kbd_init).

  2. Phase 2 — Boot protocol validation: Validate Limine boot protocol responses, compute kernel_end_phys, and configure the bootstrap processor (BSP) Model-Specific Registers (MSRs) via g_cpu_local[0].

  3. Phase 3 — Core memory and processor setup: Physical Memory Manager initialization (pmm_init), framebuffer initialization (fb_init), Virtual Memory Manager initialization (vmm_init), Local APIC (Advanced Programmable Interrupt Controller) initialization (lapic_init), Symmetric Multi-Processing initialization (smp_init), and Supervisor Mode Execution Prevention (SMEP) enable.

  4. Phase 4 — Kernel services: Kernel heap initialization (heap_init), system call initialization (syscall_init), process subsystem initialization (proc_init), jail initialization (jail_init), Virtual File System initialization (vfs_init), and root mount verification.

  5. Phase 5 — Hardware enumeration: PCI enumeration (pci_enumerate), Advanced Configuration and Power Interface (ACPI) initialization (acpi_init), block device initialization (block_init), AHCI (Advanced Host Controller Interface) initialization (ahci_init), VirtIO network initialization (virtnet_init), and network stack initialization (net_init).

  6. Phase 6 — User interface devices: User Interface (UI) initialization (uio_init), framebuffer device initialization (fbdev_init), input subsystem initialization (input_init), virtual terminal initialization (vt_init), Programmable Interval Timer (PIT) initialization (pit_init), and LAPIC timer calibration (lapic_calibrate_timer).

  7. Phase 7 — Application processor boot and entropy: BSP idle process creation, Application Processor (AP) boot (smp_boot_aps), and Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) initialization with RDRAND instruction and Timestamp Counter (TSC) fallback.

  8. Phase 8 — Interrupts and self-tests: sti (Set Interrupt Flag), PS/2 mouse initialization (ps2mouse_init), and self-tests for the PMM, VMM, and heap subsystems.

  9. Phase 9 — Filesystem and init: ext2 filesystem initialization (ext2_init), root mount, initial ramdisk (initrd) load, Filesystem Table (fstab) mount, and execution of /init.

Self-Tests

The following self-tests execute during Phase 8 to verify core subsystem integrity.

PMM Self-Test

  1. Allocate four physical pages.
  2. Map each page to a virtual address via phys_to_virt.
  3. Write the constant 0xDEADBEEFCAFEBABE to each page.
  4. Verify that each page contains a unique allocation (distinct physical addresses).

VMM Self-Test

  1. Map a single page at virtual address 0xffff900000001000.
  2. Write the constant 0xC0FFEE00DEADC0DE to the mapped page.
  3. Read back and verify the written value.
  4. Unmap the page.

Heap Self-Test

  1. Allocate buffers of 64, 128, and 256 bytes.
  2. Fill the 64-byte buffer with 0xAA, the 128-byte buffer with 0xBB, and the 256-byte buffer with 0xCC.
  3. Verify each buffer contains the expected fill pattern.
  4. Test krealloc by reallocating each buffer and verifying content preservation.

Last reviewed: 2026-07-22

Scheduling

This document describes the scheduling implementation in the Kyronix kernel. It is the child of Kernel Implementation Notes.

The Kyronix kernel uses a per-CPU round-robin scheduler with lock-free bitmap-based ready-queue selection and hardware-assisted FPU context switching.

Scheduling Algorithm

Ready Queue and Bitmap

  • Each CPU maintains a 64-bit ready bitmask (g_ready_mask) representing the priority levels with at least one runnable thread.
  • Next-thread selection uses __builtin_ctzll (Count Trailing Zeros, Long Long) on g_ready_mask to locate the lowest-numbered set bit in O(1) time.
  • Each priority level maps to a linked list of threads in the READY state.

Lock-Free Claim

  • sched_claim_next transitions a thread from READY to RUNNING using a Compare-And-Swap (CAS) operation, eliminating lock contention on the common path.
  • The CAS atomically marks the thread as RUNNING before any scheduler state is visible to other processors.

Round-Robin Fairness

  • Per-CPU arrays (g_last_scheduled) track the last scheduled thread per priority level to enforce round-robin fairness across threads of equal priority.

Context Switch

The context switch performs the following operations in sequence:

  1. Save callee-saved general-purpose registers from the outgoing thread.
  2. Execute fxsave64 to save the floating-point / SIMD state of the outgoing thread.
  3. Restore callee-saved general-purpose registers for the incoming thread.
  4. Execute fxrstor64 to restore the floating-point / SIMD state of the incoming thread.
  5. Write the FS base MSR (Model-Specific Register) for the incoming thread’s Thread-Local Storage (TLS).
  6. Load CR3 (Control Register 3) to switch to the incoming thread’s address space.

Preemption

  • Preemption is triggered on Programmable Interval Timer (PIT) IRQ 0 and Local APIC timer interrupt (vector 224).
  • The timer interrupt handler invokes the scheduler to perform a context switch if a higher-priority or equal-priority thread is runnable.

Application Processor Idle Loop

Application Processors (APs) execute the following idle loop:

  1. Call sched_claim_next to attempt to acquire a runnable thread.
  2. If a thread is claimed, call sched_switch to context-switch into it.
  3. If no thread is available, execute hlt (Halt) until the next interrupt.

Process States and Transitions

Threads transition through the following states:

UNUSED -> READY -> RUNNING -> READY
                       |-> WAITING
                       |-> ZOMBIE -> DYING
                       |-> STOPPED
  • UNUSED — Thread slot is unallocated.
  • READY — Thread is runnable and waiting for CPU time.
  • RUNNING — Thread is executing on a CPU.
  • WAITING — Thread is blocked on an event (e.g., I/O, sleep).
  • ZOMBIE — Thread has exited but its resources have not yet been reclaimed.
  • DYING — Thread is in the final stage of resource teardown.
  • STOPPED — Thread has been stopped (e.g., via signal).

Deferred Reaping

  • proc_defer_thread_reap stores a zombie thread in a pending list for deferred cleanup.
  • The next call to proc_reap_pending processes the deferred list and reclaims thread resources.
  • Deferred reaping avoids performing memory deallocation in the interrupt context where the thread transitions to ZOMBIE.

Last reviewed: 2026-07-22

IRQ Handling

This document describes the interrupt handling implementation in the Kyronix kernel. It is the child of Kernel Implementation Notes.

The Kyronix kernel dispatches interrupts through a common assembly entry point (isr_common) that supports PIC IRQs, CPU exceptions, LAPIC interrupts, and system calls.

PIC Remapping

The Programmable Interrupt Controller (PIC) is remapped to interrupt vectors 32–47 to avoid collision with CPU exception vectors 0–31.

ISR Common Entry Point

The isr_common assembly stub performs the following steps:

  1. If the interrupt originated from ring 3 (user mode), execute swapgs to switch to the kernel GS base.
  2. Push all General-Purpose Registers (GPRs) onto the kernel stack.
  3. Call the C dispatch function isr_dispatch.
  4. Pop all GPRs from the kernel stack.
  5. If returning to ring 3, execute swapgs to restore the user GS base.
  6. Execute iretq to return from the interrupt.

Interrupt Dispatch

isr_dispatch handles the following interrupt sources:

CPU Exceptions (Vectors 0–31)

  • User-mode exception: The exception is delivered as a signal to the faulting process.
  • Kernel-mode exception: The kernel invokes a panic handler with a stack backtrace.

Page Fault (Vector 14)

Page faults are handled with demand paging for the following cases:

  1. User stack growth: Faults within USER_STACK_GROW_BASE to USER_STACK_TOP trigger automatic stack expansion.
  2. VMA (Virtual Memory Area) regions: Faults within a mapped VMA region trigger demand paging via vmm_user_range_fault_in.

PIC IRQs (Vectors 32–47)

PIC IRQs are forwarded from the PIC to the corresponding vector offset.

System Call (Vector 0x80)

System calls are dispatched via the syscall instruction (vector 128).

LAPIC Timer (Vector 224)

The LAPIC (Local Advanced Programmable Interrupt Controller) timer interrupt is handled with the same preemption logic as PIC IRQ 0.

LAPIC Spurious (Vector 255)

Spurious LAPIC interrupts are acknowledged without further processing.

Timer Tick (PIC IRQ 0)

Each PIC IRQ 0 tick performs the following operations:

  1. Increment the global tick counter (g_ticks).
  2. Update the cursor blink state.
  3. Reap zombie threads.
  4. Poll the network stack.
  5. Process the timer mask for sleep/wake operations.
  6. Invoke the scheduler for preemption.

Double Fault

Double faults (vector 8) use Interrupt Stack Table (IST) stack 1, backed by a dedicated 16 KiB stack, to ensure a usable stack is available when the primary stack is corrupted.

Non-Maskable Interrupt (NMI)

Non-Maskable Interrupts (NMIs, vector 2) use IST stack 2, backed by a dedicated 16 KiB stack, to handle hardware-critical conditions independently of normal interrupt processing.

Kernel Backtrace

The kernel backtrace routine scans the kernel stack for return addresses within the kernel text region defined by the bounds:

[0xffffffff80000000, 0xffffffff80040000)

Any pointer found within this range is reported as a valid kernel return address.

Last reviewed: 2026-07-22

Memory Management

This document describes the memory management implementation details in the Kyronix kernel. It is the child of Kernel Implementation Notes.

The Kyronix kernel implements a layered memory management architecture comprising a Physical Memory Manager (PMM), Virtual Memory Manager (VMM), kernel heap, Shared Memory (SHM) subsystem, and a runtime memory leak detector.

Physical Memory Manager (PMM)

Initialization

  1. Parse the Limine boot protocol memory map to identify usable physical memory regions.
  2. Find the highest usable physical address across all regions.
  3. Compute the total number of physical frames from the highest usable address.
  4. Allocate LL-Free metadata from the largest contiguous usable region.

LL-Free Three-Tier Architecture

The LL-Free allocator organizes physical frames into a three-tier hierarchy:

  1. Frame: A single 4 KiB physical page.
  2. Child: A group of 512 frames, totaling 2 MiB.
  3. Tree: A group of 8 children, totaling 4096 frames (16 MiB).

CPU-Local Reservations

Each CPU reserves one tree from the LL-Free allocator for lock-free allocation, eliminating cross-CPU contention on the fast path.

Zero-Page Pool

A pool of 32 pre-allocated zeroed physical pages provides fast allocation for pmm_alloc_zeroed without requiring a memset on each allocation.

Virtual Memory Manager (VMM)

Page Table Management

The VMM performs a 4-level page table walk (PML4 → PDPT → PD → PT) to resolve and manipulate virtual-to-physical address mappings.

Demand Paging

Unmapped pages within valid Virtual Memory Area (VMA) regions trigger demand paging via vmm_user_range_fault_in, which allocates physical frames and installs page table entries on demand.

Virtual Memory Areas (VMA)

  • VMAs are stored in a flat array of 2048 entries per process.
  • Lookups use a linear scan of the array.
  • split_for_hole splits an existing VMA into two smaller VMAs when a region must be removed from the middle of a mapping.

Kernel Heap

  • The heap uses a first-fit allocator with a doubly-linked free list.
  • Coalescing is performed in both the forward and backward directions on free.
  • All allocations are aligned to 16-byte boundaries.
  • The heap grows in 64 KiB increments from the initial allocation.

Shared Memory (SHM)

  • Shared Memory follows the System V Inter-Process Communication (IPC) model.
  • A maximum of 64 SHM segments are supported.
  • Jail isolation is enforced via the JAILF_IPC flag, which prevents processes within a jail from accessing SHM segments outside the jail.

Memory Leak Detector (kmemleak)

The kmemleak subsystem implements a mark-and-sweep leak detector that scans the following memory regions for allocated pointers:

  1. .data section — Initialized global and static variables.
  2. .bss section — Uninitialized global and static variables.
  3. Process table — All live process structures.
  4. Heap — All live heap allocations.
  5. Page tables — All mapped page table entries.

Last reviewed: 2026-07-22

Driver Implementation Notes

This document provides implementation notes for the Kyronix kernel drivers. It is the child of Implementation Notes.

The driver implementation notes cover internal details for hardware discovery and device initialization, including bus enumeration and device driver probe sequences.

Subsections

  • PCI Enumeration — Peripheral Component Interconnect (PCI) bus enumeration

Last reviewed: 2026-07-22

PCI Enumeration

This document describes the PCI bus enumeration implementation in the Kyronix kernel. It is the child of Driver Implementation Notes.

The Kyronix kernel enumerates the PCI bus using Port I/O-based configuration space access to discover and catalog all connected hardware devices.

Configuration Space Access

PCI configuration space is accessed through two standard I/O ports:

  • 0xCF8 (Address register): Receives the configuration address, which encodes the bus number, device number, function number, and register offset.
  • 0xCFC (Data register): Provides read/write access to the configuration data at the specified address.

Enumeration Procedure

  1. Iterate over all 256 buses.
  2. For each bus, iterate over all 32 devices.
  3. For each device, iterate over all 8 functions.
  4. Read the Vendor ID register at offset 0x00.
  5. If the Vendor ID returns 0xFFFF, the device or function is absent; skip to the next function.
  6. Read the Class Code, Subclass, Programming Interface (prog_if), Base Address Register (BAR) registers, and Header Type from the configuration space.
  7. Store the device information in the global device table.

Usage

The global device table populated by PCI enumeration is consumed by the following drivers to discover and initialize their respective hardware:

  • AHCI (Advanced Host Controller Interface) driver
  • VirtIO driver
  • Other PCI device drivers that query the device table during their probe sequences

Last reviewed: 2026-07-22

Reference

This document provides reference material for the Kyronix kernel. It is the root of the Reference section.

Subsections

  1. Syscalls
  2. Protocols
  3. API

Last reviewed: 2026-07-22

Syscalls Reference

This document provides a complete reference of all syscalls implemented in the Kyronix kernel. It is the child of Reference.

Overview

The kernel implements approximately 140 Linux x86_64 ABI-compatible syscalls plus custom jail syscalls.

Syscall Table

#Name#Name
0read1write
2open3close
4stat5fstat
6lstat7poll
8lseek9mmap
10mprotect11munmap
12brk13rt_sigaction
14rt_sigprocmask15rt_sigreturn
16ioctl17pread64
18pwrite6419readv
20writev21access
22pipe23select
25mremap29shmget
30shmat31shmctl
32dup33dup2
34pause35nanosleep
36getitimer37alarm
38setitimer39getpid
40sendfile41socket
42connect43accept
44sendto45recvfrom
46sendmsg47recvmsg
48shutdown49bind
50listen51getsockname
52getpeername53socketpair
54setsockopt55getsockopt
56clone57fork
58vfork59execve
60exit61wait4
62kill63uname
67shmdt72fcntl
76truncate77ftruncate
78getdents6479getcwd
80chdir82rename
83mkdir84rmdir
86link87unlink
88symlink89readlink
90chmod95umask
96gettimeofday97getrlimit
101ptrace102getuid
104getgid105setuid
106setgid107geteuid
108getegid109setpgid
112setsid117setresuid
119setresgid137statfs
158arch_prctl169reboot
186gettid201time
202futex213epoll_create
218set_tid_address228clock_gettime
229clock_getres230clock_nanosleep
231exit_group232epoll_wait
233epoll_ctl234tgkill
257openat262newfstatat
263unlinkat270pselect6
271ppoll280openat2
283timerfd_create284eventfd
288accept4290eventfd2
291epoll_create1292dup3
293pipe2295preadv
296pwritev302prlimit64
318getrandom319memfd_create
326copy_file_range332statx
334close_range

Custom Jail Syscalls

#Name
500jail_create
501jail_attach
502jail_get
503jail_list
504jail_remove
505jail_self
506jail_set_auto

Last reviewed: 2026-07-22

Protocols

This document describes the protocols supported by the Kyronix kernel. It is the child of Reference.

Supported Protocols

  • Limine v3 boot protocol: memory map, HHDM (Higher Half Direct Map), framebuffer, modules, RSDP (Root System Description Pointer), SMP (Symmetric Multi-Processing), kernel address.
  • PCI configuration: standard I/O configuration space (ports 0xCF8/0xCFC).
  • ACPI (Advanced Configuration and Power Interface): RSDP, RSDT/XSDT, MADT (Multiple APIC Description Table), FADT (Fixed ACPI Description Table).
  • AHCI (Advanced Host Controller Interface): SATA (Serial ATA) host controller interface.
  • VirtIO 1.0: paravirtualized network device.
  • lwIP TCP/IP (lightweight IP): IPv4 (Internet Protocol version 4), TCP (Transmission Control Protocol), UDP (User Datagram Protocol), ICMP (Internet Control Message Protocol), ARP (Address Resolution Protocol).
  • PS/2: keyboard and mouse input.
  • Syscall: AMD64 SYSCALL/SYSRET mechanism.

Last reviewed: 2026-07-22

API

This document describes the internal kernel API surface of the Kyronix kernel. It is the child of Reference.

Memory

  • pmm_alloc - allocate a physical page
  • pmm_alloc_zeroed - allocate a zeroed physical page
  • pmm_alloc_contiguous - allocate contiguous physical pages
  • pmm_free - free a physical page
  • vmm_map - map a virtual address
  • vmm_unmap - unmap a virtual address
  • vmm_protect - set protection flags on a mapping
  • vmm_space_new - create a new virtual address space
  • vmm_space_free - free a virtual address space
  • vmm_switch - switch to a virtual address space
  • vmm_fork_user - fork user address space
  • kmalloc - kernel memory allocate
  • kcalloc - kernel memory calloc
  • krealloc - kernel memory reallocate
  • kfree - kernel memory free

Process

  • proc_alloc - allocate a process
  • proc_ref - increment process reference count
  • proc_unref - decrement process reference count
  • proc_find - find a process by PID (Process ID)
  • proc_do_exit - terminate a process
  • sched_switch - context switch to another process
  • sched_claim_next - claim the next runnable process

Filesystem

  • vfs_init - initialize the Virtual File System (VFS)
  • vfs_lookup - look up a path in the VFS
  • vfs_mount - mount a filesystem
  • vfs_node_unref_internal - unreference an internal VFS node
  • vfs_sync_all - synchronize all mounted filesystems

Signals

  • proc_send_signal - send a signal to a process
  • signal_check - check for pending signals

Jail

  • jail_init - initialize the jail subsystem
  • jail_create - create a new jail
  • jail_enter - enter a jail
  • jail_remove - remove a jail
  • jail_can_see - check if a process can see another process
  • jail_host_priv - check if a process has host privileges

Crypto

  • chacha20_rng_init - initialize the ChaCha20 random number generator
  • chacha20_rng_bytes - generate random bytes

Last reviewed: 2026-07-22