Skip to content

irqlevel/nos

Repository files navigation

nos

A hobby operating system kernel for x86-64 and arm64 (aarch64), written in C++20, Rust, and assembly. Code is partially written by AI models (Claude, GPT) with human control over architecture decisions, testing, and code review. Tested primarily in QEMU/KVM environments, including Google Cloud and Yandex Cloud VMs (MBR-based disk image, virtio devices), and on QEMU virt with HVF acceleration on Apple Silicon for the arm64 build. Real hardware support has not been tested.

Features

  • Two architectures — x86-64 (Multiboot2/GRUB, ISO or MBR disk boot) and arm64 (QEMU virt board, Linux Image boot protocol); portable code goes through a HAL layer (src/cpp/hal/), arch backends live in src/cpp/arch/
  • SMP — up to 8 CPUs; AP bootstrap via INIT/SIPI on x86-64, PSCI CPU_ON on arm64
  • Preemptive multitasking — per-CPU task queues, round-robin scheduling, load-balanced task placement
  • Virtual memory — 4-level paging (4 KB pages), high-half kernel at 0xFFFF800001000000, TLB shootdown across CPUs via IPI
  • Page allocator — fixed-size block allocator (1–128 contiguous pages), pool allocator (32 B – 2 KB), new/delete support
  • ACPI — RSDP/RSDT/MADT parsing for LAPIC/IOAPIC discovery and IRQ→GSI routing
  • Interrupts — IDT with exception handlers, IOAPIC routing (edge + level-triggered), LAPIC IPI, PIC (remapped then disabled)
  • arm64 port — GICv3 interrupt controller with ITS (PCIe MSI delivered as LPIs, its=on by default), EL1 exception vectors, ARM generic timer (per-CPU), PL011 UART, FDT (device tree) parsing, PCIe ECAM, virtio-mmio transport, broadcast TLBI, semantic memory barriers (dmb) throughout; NVMe over ITS-delivered MSI works end-to-end
  • Drivers — serial (COM1), VGA text mode, PIT (10 ms tick, SeqLock-protected counters), RTC (CMOS wall clock), PS/2 keyboard (8042), PCI bus scan, LAPIC, IOAPIC, virtio-blk, virtio-net, virtio-scsi, virtio-rng (legacy + modern virtio-pci transport), NVMe (Rust, MSI-X interrupt-driven)
  • Block I/O — asynchronous, interrupt-driven block request queue with DMA slot pool, BlockRequest submission with WaitGroup completion, direct DMA from caller buffers (page-aligned), virtqueue locking (RawSpinLock) for safe interrupt/task concurrency, early-boot polling fallback, SoftIrq-based retry for ring-full conditions, block device abstraction, MBR partition discovery
  • Networking — virtio-net driver with asynchronous interrupt-driven TX/RX, software frame queues (256-entry TX/RX) in NetDevice base class, reference-counted NetFrame descriptors for zero-copy DMA, TX slot pool with bitmask allocation, SoftIrq-based TX retry and RX processing, IP routing (subnet mask + gateway from DHCP, off-subnet traffic forwarded to gateway), ARP (cache, request, reply, dump), IPv4/UDP transmit, ICMP echo (ping reply + send, per-type statistics), DHCP client with lease renewal (sets IP, subnet mask, gateway, DNS server), DNS resolver with 32-entry cache (A-record queries, name compression, DHCP-provided server), TCP (connection state machine, 3-way handshake, sequence/ack tracking, retransmission timers, MSS negotiation, send/receive ring buffers, graceful close with FIN exchange, RST handling, ephemeral port allocation, granular locking: Mutex for ports, RawSpinLock for pool and per-connection state, SoftIrq-driven timer processing), HTTP client (URL parsing, DNS resolution, TCP connection, request/response, redirect following for 301/302/303/307/308 with loop limit, wget shell command), UDP remote shell (execute kernel commands over the network), network device abstraction with per-protocol packet counters, MacAddress/IpAddress structs (IPv6-ready tagged union)
  • Filesystem — VFS layer with mount points and path resolution, ramfs (in-memory), nanofs (on-disk filesystem with 4 KB blocks, superblock with UUID, inode/data bitmaps, CRC32 checksums for superblock/inodes/data, file and recursive directory deletion, persistent across remount)
  • EntropyEntropySource interface, EntropySourceTable registry, virtio-rng hardware random number generator
  • Power management — ACPI S5 shutdown, keyboard controller reset/reboot
  • Interactive shell — trace output suppressed during shell session (dmesg only), restored on shutdown; commands: ps, cpu, bt <pid>, dmesg [filter], uptime, date, memusage, pci, disks, diskread, diskwrite, irqstat, net, arp, icmpstat, tcpstat, udpsend, ping, nslookup, dnsflush, dhcp, wget, random, format, mount, umount, ls, cat, write, mkdir, touch, del, panic, version, cls, help, poweroff, reboot
  • Timekeeping — TSC calibration via PIT channel 2 (multi-round median), KVM paravirt clock (kvmclock) for accurate VM time, RTC wall clock, layered clock source selection (kvmclock → calibrated TSC → PIT fallback), GetBootTime() / GetWallTimeSecs() API
  • Kernel infrastructure — spinlocks, mutexes, SeqLock (single-writer/multi-reader), atomics, wait groups, SoftIrq deferred processing, IPI tasks, timers, watchdog, stack traces with symbol resolution, dmesg ring buffer (512 KB, 2048 messages), panic handler with backtrace and CPU/task context, per-device interrupt statistics, AP startup diagnostics, virtual-to-physical address translation (4-level page table walk), byte-order helpers (Htons/Htonl/Ntohs/Ntohl)
  • Optimized stdlibMemSet, MemCpy, MemCmp, StrLen, StrCmp, StrStr implemented in x86-64 assembly using rep stosq/rep movsq/repe cmpsb/repne scasb (portable C versions on arm64)
  • Rust support#![no_std] Rust crates linked into the kernel via staticlib, FFI bridge (rust_ffi.cpp) exposing kernel services to Rust: spinlocks, mutexes, wait groups, timers, SoftIRQ, MSI-X interrupts, legacy interrupts, DMA allocation, MMIO mapping, PCI config space, block device and network device registration, CPU/IPI/task APIs. kcore library provides safe Rust wrappers around kernel primitives. NVMe driver written entirely in Rust — PCI BAR mapping, admin + I/O queue pairs, MSI-X interrupt-driven completion, WaitGroup-based synchronous I/O, multi-device support, proper RAII cleanup on shutdown
  • Boot tests — allocator, btree, ring buffer, stack trace, multitasking, contiguous page alloc (up to 128 pages), parsing helpers, block device table, memset, memcpy, memcmp, strlen, strcmp, strstr

Build

The build is parameterized by ARCH (default x86_64, or aarch64); objects go to out/$(ARCH)/.

Native (requires clang, nasm, ld, grub-mkrescue, rustup with x86_64-unknown-none target):

make

Via Docker (works on macOS / Apple Silicon):

./scripts/build-iso-docker.sh

This produces nos.iso and bin/kernel64.elf (for GDB symbols).

arm64 (requires ld.lld, llvm-nm, rustup with aarch64-unknown-none-softfloat target; on macOS build in Docker):

make nocheck ARCH=aarch64

This produces kernel-arm64.elf and nos-arm64.img (Linux Image format, bootable with QEMU -kernel).

Build a bootable qcow2 disk image (MBR, 2 partitions):

./scripts/build-disk.sh

This produces nos.qcow2 (1 GB, MBR, virtio-blk compatible, suitable for KVM-based public clouds including Google Cloud Compute Engine).

Run

With KVM (Linux):

qemu-system-x86_64 -enable-kvm -smp 8 -cdrom nos.iso -serial file:nos.log \
    -device isa-debug-exit,iobase=0xf4,iosize=0x04

Without KVM (macOS with TCG):

qemu-system-x86_64 -smp 2 -cdrom nos.iso -serial file:nos.log -s -vga std

Boot from disk image (with virtio-blk):

./scripts/qemu-disk.sh

arm64 on QEMU virt (HVF-accelerated on Apple Silicon, NOS_TCG=1 forces TCG; virtio-mmio blk/net/rng attached, serial goes to nos-arm64.log, UDP shell forwarded on :9000):

./scripts/qemu-arm64.sh
./scripts/smoke-arm64.sh              # boot smoke test (SMOKE_HVF=1 for HVF)
python3 scripts/udpsh.py 127.0.0.1    # remote shell over UDP

Deploy to Google Cloud Compute Engine (select Skip OS adaptation when importing the image — the kernel already has the necessary drivers and no guest agent):

# Upload disk image to a GCS bucket
gcloud storage cp nos.qcow2 gs://YOUR_BUCKET/nos.qcow2

# Create a Compute Engine image from the disk (skip OS adaptation)
gcloud compute images create nos-image \
    --source-uri=gs://YOUR_BUCKET/nos.qcow2

# Launch a VM (serial console recommended)
gcloud compute instances create nos-vm \
    --image=nos-image \
    --machine-type=e2-small \
    --metadata=serial-port-enable=true

# Connect via serial console
gcloud compute connect-to-serial-port nos-vm

Debug

Start QEMU with -s (GDB server on port 1234), then:

gdb -ex "symbol-file bin/kernel64.elf" \
    -ex "set architecture i386:x86-64" \
    -ex "target remote :1234"

arm64 (start ./scripts/qemu-arm64.sh -s, needs gdb-multiarch):

./scripts/gdb-arm64.sh

Kernel parameters

Pass via GRUB command line on x86-64 (edit build/grub.cfg) or QEMU -append on arm64:

  • smp=off — disable SMP, run on BSP only
  • console=serial — direct shell output to serial port only
  • console=vga — direct shell output to VGA only
  • dhcp=auto — start DHCP on eth0 automatically at boot
  • dhcp=off — disable DHCP entirely (even via shell command)
  • dhcp=on — enable DHCP only via shell command (default)
  • dns=on — enable DNS resolver (uses DHCP-provided DNS server; requires dhcp=auto)
  • udpshell=PORT — start UDP remote shell on the given port (e.g. udpshell=9000)
  • its=off — arm64 only: disable the GICv3 ITS and degrade PCIe MSI gracefully (default its=on; virtio-mmio devices don't need it)

UDP remote shell

When udpshell=PORT is set, the kernel listens for commands on that UDP port. A lightweight protocol header (16 bytes: magic, sequence number, chunk index, flags, payload length) frames every packet, enabling the client to validate replies, reassemble multi-chunk responses in order, and detect the end of a response without relying on timeouts.

Connect with the included Python client:

python3 scripts/udpsh.py <vm-ip> [port] [timeout]
  • Default port is 9000, default timeout is 30 seconds (long enough for blocking commands like ping).
  • On protocol errors or timeouts, the client reconnects automatically and resets state.
  • All shell commands work over the UDP session (including blocking ones like ping).
  • Warning: the UDP shell has no authentication — anyone who can reach the port has full kernel shell access. Use only for testing or behind a firewall.

Shell commands

Command Description
cls Clear screen
cpu Dump CPU state
dmesg [filter] Dump kernel log (optional substring filter)
uptime Show uptime
date Show wall clock date and time (RTC + boot time)
ps Show tasks
bt <pid> Dump stack trace of a task (uses IPI for remote CPUs)
watchdog Show watchdog stats
memusage Show memory usage
pci Show PCI devices
disks List block devices
diskread <disk> <sector> Read and hex-dump a sector
diskwrite <disk> <sector> <hex> Write hex data to a sector
irqstat Show per-device interrupt counters
help List commands
net List network devices and per-protocol stats
arp Show ARP table
icmpstat Show ICMP statistics
tcpstat Show TCP connections and statistics
wget <url> Fetch a URL via HTTP GET (follows redirects)
udpsend <ip> <port> <msg> Send a UDP packet
ping <ip|hostname> Send 5 ICMP echo requests with RTT (resolves hostnames via DNS)
nslookup <hostname> Resolve hostname to IP via DNS
dnsflush Flush DNS cache
dhcp [dev] Obtain IP address via DHCP
random [len] Get random bytes as hex string
format nanofs <disk> Format disk with nanofs
mount ramfs <path> Mount a ramfs at path
mount nanofs <disk> <path> Mount nanofs from disk at path
umount <path> Unmount filesystem
mounts List mount points
ls <path> List directory contents
cat <path> Show file contents
write <path> <text> Write text to file (creates if needed)
mkdir <path> Create directory
touch <path> Create empty file
del <path> Remove file or directory
panic [type] Trigger kernel panic (direct, pagefault, divzero, ud)
version Show kernel version
poweroff / shutdown Power off (ACPI S5)
reboot Reset system (keyboard controller)

Project layout

src/cpp/
  hal/        Portable HAL contracts: cpu, barriers, mmu/pte, irqchip, console, power, context, irq stubs
  arch/
    x86_64/   Multiboot2 entry + AP trampoline (NASM), CPU primitives (asm.asm), IDT/GDT, exceptions, TSC/kvmclock, LAPIC/IOAPIC/PIC, PTE encoding, GRUB info parsing, HAL backends
    arm64/    Linux-Image boot + PSCI SMP (boot.S), EL1 vectors, GICv3 + ITS (LPIs for PCIe MSI), generic timer, PL011, FDT parser, PCIe ECAM, PTE encoding, HAL backends
  kernel/     Core: scheduling, tasks, interrupt dispatch, SoftIrq, shell, timers, timekeeping, locks, panic, Rust FFI bridge, symbol table
  drivers/    Hardware: serial, VGA, PIT, HPET, RTC, 8042, PCI, MSI-X, ACPI, virtio blk/net/scsi/rng (virtio-pci on x86-64, virtio-mmio on arm64)
  block/      Block I/O: device abstraction, async request queue, MBR partition discovery
  net/        Networking: device abstraction, protocol headers, ARP, ICMP, DHCP, DNS, TCP, HTTP client, UDP shell
  fs/         Filesystem: VFS, ramfs, nanofs, block I/O helpers
  mm/         Memory: page tables (4-level walk, VirtToPhys), page allocator, pool allocator
  lib/        Utilities: list, vector, btree, ring buffer, bitmap, CRC32 checksum, stdlib
  include/    Shared headers
src/rust/
  ffi/        Raw extern "C" FFI declarations for kernel services
  kcore/      Safe Rust wrappers: sync, DMA, MMIO, MSI-X, interrupts, timers, tasks, PCI, block/net device
  drivers/
    nvme/     NVMe block device driver (PCI, MSI-X, admin/IO queues)
    r8168/    Realtek r8168 network device driver
  hello/      Rust self-test module
  kernel/     Rust entry points (rust_main, rust_fini), global allocator
build/        Linker script, GRUB configs
scripts/      Build, run, debug, and GDB helpers

Releases

Packages

Contributors

Languages