
# Contributing to SUB-OS
Thanks for your interest in **SUB-OS** — a multi-architecture, modular monolithic,
Linux-compatible operating system written from scratch in C, C++17, Assembly,
freestanding Rust (`no_std`), and the native SUB Language. This guide covers how
to build, the conventions we follow, and how to get a change merged.
---
## Table of Contents
- [Ground rules](#ground-rules)
- [Development environment](#development-environment)
- [Building](#building)
- [Running & verifying](#running--verifying)
- [Coding style](#coding-style)
- [Kernel gotchas](#kernel-gotchas)
- [Commit & PR conventions](#commit--pr-conventions)
- [Reporting bugs](#reporting-bugs)
---
## Ground rules
- **Verify before you claim.** Nothing is "done" until it builds *and* boots.
Every crypto primitive and codec is tested against a reference vector (RFC or
a PIL-generated fixture) **before** it enters the kernel. Hold new code to the
same bar.
- **No libc, no POSIX host.** The kernel is freestanding. No floating point in
kernel space (`-mno-sse`); all math is integer.
- **Small, reviewable changes.** One feature or fix per pull request, with a
clear description of what you verified and how.
- **Be kind.** Assume good faith, keep discussion technical, and respect
reviewers' time.
---
## Development environment
The project targets `x86_64`, `aarch64`, and `armv8i` (AArch32). On a Debian/
Ubuntu host, install the toolchains and emulators:
```bash
sudo apt update
sudo apt install build-essential g++ nasm qemu-system-x86 qemu-system-arm \
gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu \
gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf \
rustc cargo dialog python3
```
For the freestanding Rust layer:
```bash
rustup target add x86_64-unknown-none
```
> The x86_64 kernel is normally built with an `x86_64-elf-` cross toolchain. A
> stock `x86_64-linux-gnu-` GCC also works — pass it via `CROSS_COMPILE=`.
---
## Building
SUB-OS uses a Linux-style Kconfig + Kbuild flow. The version/codename lives at
the top of the `Makefile` (`VERSION`/`PATCHLEVEL`/`SUBLEVEL`/`EXTRAVERSION`/
`NAME`) and in [`include/init/version.h`](../include/init/version.h) — keep the
two in sync when you bump a release.
```bash
# Configure (interactive) or pick an architecture preset:
make configure # or: make menuconfig
make x86_64_defconfig # x86_64 (Intel / AMD)
make aarch64_defconfig # 64-bit ARM (Cortex-A57)
make armv8i_defconfig # 32-bit ARM (Cortex-A15)
# Build:
make # builds the configured/default ARCH
make ARCH=aarch64 # or target one explicitly
```
> **Always `make clean` when switching `ARCH`.** Object files from another
> architecture link into a *"file in wrong format"* error otherwise.
---
## Running & verifying
```bash
make run # x86_64 disk image in QEMU
make run-gui # windowed graphical desktop
make run-fullscreen # fullscreen desktop
make run-vnc # headless: VNC on localhost:5900
make ARCH=aarch64 run # ARM targets
```
Default login is **`SUB` / `SUB`**. Pass `nogui`, `text`, `emergency`, or
`single` on the kernel command line to boot to the TTY instead of the desktop.
**Headless verification** (what CI and reviewers use): boot with
`-serial file:serial.log -monitor unix:mon.sock,server,nowait -display none`,
drive the login over the monitor `sendkey` interface, and capture the screen
with `screendump`. For a code path with no UI, add a temporary CLI applet under
`userland/lazybox/` that exercises it over serial, verify, then revert the
temporary hook.
When you submit a change, state plainly **what you ran and what you saw** — build
output, boot log excerpt, screenshot, or reference-vector comparison.
---
## Coding style
- **C:** freestanding C11. Match the surrounding file — 4-space indent, K&R
braces, `snake_case` for functions and variables, `SCREAMING_SNAKE` for
macros/constants. Keep comment density and idiom consistent with the module
you're editing.
- **Assembly:** per-arch under `arch//`. `.S` files run through `cpp`, so
**no nested `/* ... */` comments**.
- **Rust:** `no_std`, no allocator assumptions beyond what the kernel provides.
- **C++:** freestanding C++17, no exceptions/RTTI, no host STL.
- **Headers:** public interfaces in `include/`, with an include guard.
- **No new external dependencies** in kernel space.
---
## Kernel gotchas
These bite newcomers — internalize them before touching the relevant subsystem:
- **`printk`/`vsprintf` are minimal.** Supported conversions are
`c s d i u o x X p %` only — **no** length modifiers (`l`, `ll`) and **no**
field width. Cast to `int` / `unsigned` / `void*` at the call site.
- **`list_del()` NULLs both links**, so `list_empty()` is not a reliable "is
this node still queued?" test after a delete — inspect the links directly.
- **Preemption is armed after boot.** A context switch must never run while a
plain spinlock is held: wrap such sections with
`sched_preempt_disable/enable` (as `printk` does). `weave_lock` sections use
`arch_irq_save/restore` so the timer tick cannot fire inside them.
- **Dead tasks/stacks are not reclaimed yet.** Prefer one long-lived worker
thread over spawning a task per request.
- **Network checksums are byte-wise on purpose** — word-at-a-time reads get
reordered around the caller's `hdr->checksum = 0` store under `-O2`.
- **Ethernet frames are padded to 60 bytes** — trim payloads to the IP
`total_length`, never the frame length.
---
## Commit & PR conventions
- **Commit messages** follow Conventional-Commits-style prefixes:
`feat(scope): …`, `fix(scope): …`, `build: …`, `docs: …`, `release(vX.Y.Z): …`.
Write the subject in the imperative and add a body explaining *what changed
and what you verified*.
- **Branch** off `main` for your work; open a pull request against `main`.
- **One logical change per PR.** Rebase/squash noisy WIP commits before review.
- **CI must pass** (build + kernel verification) before merge.
- In the PR description, include: what the change does, which architectures you
built, and how you verified boot behaviour.
---
## Reporting bugs
Open a GitHub issue with:
1. **Architecture** and how you built (`defconfig`, `ARCH`, toolchain).
2. **Exact command** that reproduces it.
3. **What you expected** vs. **what happened** — paste the serial log and, for
the desktop, attach a `screendump`.
4. The commit SHA (`git rev-parse --short HEAD`).
For **security-sensitive** reports, do **not** open a public issue — follow the
[Security Policy](SECURITY.md) instead.
---
Happy hacking. 🐉