SchOS/docs/Website/index.html
Nexoniarz ed81f9a07a Schcom is gone; the desktop path is Xorg now, Wayland groundwork next
SchOS will not write its own compositor. projects/schcom, its test and its make targets are deleted. roadmap.md section 5 is
now the road to Wayland (udev/eudev, a /sys/devices tree and uevent netlink, libinput and libseat, a DRM device via the GPU
plan) and 5b is the boot manager rework (Niveus/skeuomorphic UEFI GUI). XLibre, Xorg or a Wayland compositor from Alpine,
whichever suits; Xorg for now.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
2026-10-26 09:35:54 +01:00

786 lines
38 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SchOS — every layer its own</title>
<meta name="description" content="SchOS is a from-scratch x86-64 operating system: the SchIFT kernel, the SchFS copy-on-write filesystem, a UEFI loader, and a userland that runs unmodified musl and toybox.">
<link rel="icon" href="Assets/shift.svg" type="image/svg+xml">
<!-- Niveus, and its optional continuity helpers. -->
<link rel="stylesheet" href="niveus/dist/niveus.css">
<script src="niveus/niveus/niveus.js" defer></script>
<!-- This site's own layout. No colours, no shadows, no type sizes. -->
<link rel="stylesheet" href="app.css">
<script>
/* Before first paint, so the page never renders in one appearance and then
corrects itself. Dark is the default because the OS is dark; the toggle
is real, and what it chooses is remembered. */
try {
var saved = localStorage.getItem("schos-theme");
if (saved === "system") document.documentElement.removeAttribute("data-theme");
else if (saved) document.documentElement.setAttribute("data-theme", saved);
} catch (e) { /* storage unavailable — keep the default */ }
</script>
<!-- The whole identity, and it is not a design decision: SCH_ACCENT, copied
from include/gfx/theme.h. A site describing an operating system should look
like the operating system. -->
<style>
:root { --nv-brand: #7C5CFF; }
</style>
</head>
<body>
<nav class="nv-nav" id="topnav" aria-label="Main" style="--nv-selection-name: nv-schos-nav">
<a class="nv-nav__brand" href="#top">
<img class="nv-nav__icon" src="Assets/shift.svg" alt="">
SchOS
</a>
<ul class="nv-nav__list">
<li><a class="nv-nav__item" href="#origin">Origin</a></li>
<li><a class="nv-nav__item" href="#stack">The stack</a></li>
<li><a class="nv-nav__item" href="#what">What it has</a></li>
<li><a class="nv-nav__item" href="#hybrid">Hybrid</a></li>
<li><a class="nv-nav__item" href="#numbers">Numbers</a></li>
<li><a class="nv-nav__item" href="#wallpapers">Wallpapers</a></li>
</ul>
<span class="nv-nav__spacer"></span>
<a class="pride" href="#pride-note"
title="SchOS stands with the LGBTQIAP+ community — every month of the year">
<img src="Assets/pride.svg" alt="Progress Pride flag">
</a>
<div class="nv-button-group appearance" role="group" aria-label="Appearance">
<button class="nv-button nv-button--sm" data-nv-theme="light" aria-pressed="false">Light</button>
<button class="nv-button nv-button--sm" data-nv-theme="dark" aria-pressed="true">Dark</button>
<button class="nv-button nv-button--sm" data-nv-theme="system" aria-pressed="false">Auto</button>
</div>
<a class="nv-button nv-button--sm" href="docs.html">Docs</a>
<a class="nv-button nv-button--sm nv-button--primary" href="download.html">Download</a>
</nav>
<main id="top">
<!-- ====================================================================== -->
<!-- The one full-bleed band on the page, lit like the Graphite wallpaper. -->
<header class="heroband">
<div class="shell hero">
<div class="hero__copy">
<span class="nv-badge nv-badge--accent">
<span class="nv-badge__dot"></span>
SchIFT 0.1.0 · boots on real hardware
</span>
<h1 class="nv-display hero__title">
<span class="nv-tile nv-tile--xl hero__tile"><img src="Assets/shift.svg" alt=""></span>SchOS
</h1>
<p class="nv-heading hero__slogan">Every layer its own — from the first instruction to the last pixel.</p>
<p class="nv-prose">
A 64-bit operating system written from scratch — its own kernel, its own
filesystem, its own bootloader. It boots on UEFI, drives real hardware,
and runs unmodified <code class="nv-code">musl</code> and
<code class="nv-code">toybox</code> on top.
</p>
<div class="nv-cluster">
<a class="nv-button nv-button--primary nv-button--lg" href="download.html">Download it</a>
<a class="nv-button nv-button--lg" href="#what">What it has</a>
</div>
<p class="nv-caption">GPL-2.0-or-later · runs from RAM · writes nothing to your disks</p>
</div>
<div class="nv-window console" aria-label="Boot log">
<div class="nv-window__titlebar">
<span class="nv-window__controls" aria-hidden="true">
<span class="nv-window__control nv-window__control--close"></span>
<span class="nv-window__control nv-window__control--minimize"></span>
<span class="nv-window__control nv-window__control--zoom"></span>
</span>
<span class="nv-window__title">SchIFT — ttyS0</span>
<span class="nv-window__actions">
<span class="nv-badge nv-badge--success"><span class="nv-badge__dot"></span>live</span>
</span>
</div>
<pre class="nv-window__body nv-code console__log" id="bootlog" aria-live="polite"></pre>
</div>
</div>
</header>
<!-- ====================================================================== -->
<section id="origin" class="shell section" data-reveal>
<div class="section__head">
<p class="nv-overline nv-text-accent">Origin</p>
<h2 class="nv-heading">A teaching kernel that outgrew its lessons</h2>
<p class="nv-prose nv-text-secondary">
SchOS began as <a href="https://github.com/mit-pdos/xv6-public">xv6</a>, MIT's
teaching operating system: 32-bit, single core, one filesystem, a few dozen
syscalls. Almost none of that survives. What follows is what replaced it.
</p>
</div>
<!-- A timeline, not a grid: these happened in this order, and the order is
the point. The rail is what carries that; the labels are its steps. -->
<ol class="nv-steps nv-steps--vertical origin">
<li class="nv-steps__item" data-state="done">
<span class="nv-steps__marker">1</span>
<div class="nv-steps__body">
<span class="nv-overline nv-text-tertiary">First</span>
<h3 class="nv-steps__title">64-bit, and more than one core</h3>
<p class="nv-steps__text">
Four-level paging, a real SMP bring-up, per-CPU state and a trap path
that survives both. This is the part that invalidates most of what a
32-bit uniprocessor kernel assumes.
</p>
</div>
</li>
<li class="nv-steps__item" data-state="done">
<span class="nv-steps__marker">2</span>
<div class="nv-steps__body">
<span class="nv-overline nv-text-tertiary">Then</span>
<h3 class="nv-steps__title">Its own filesystem</h3>
<p class="nv-steps__text">
xv6's log-structured filesystem was replaced by <b>SchFS</b>: a
copy-on-write B-tree with an uberblock ring, extents and checksummed
metadata. A crash leaves either the old tree or the new one.
</p>
</div>
</li>
<li class="nv-steps__item" data-state="done">
<span class="nv-steps__marker">3</span>
<div class="nv-steps__body">
<span class="nv-overline nv-text-tertiary">Then</span>
<h3 class="nv-steps__title">A Linux-shaped ABI</h3>
<p class="nv-steps__text">
The syscall table adopted Linux numbering, which is what makes an
unmodified musl and an unmodified toybox run. The table is one
<code class="nv-code">.inc</code> file, and it is the seam the whole
system is built around.
</p>
</div>
</li>
<li class="nv-steps__item" data-state="done">
<span class="nv-steps__marker">4</span>
<div class="nv-steps__body">
<span class="nv-overline nv-text-tertiary">Then</span>
<h3 class="nv-steps__title">Hardware, not emulation</h3>
<p class="nv-steps__text">
A UEFI loader, xHCI and USB storage, an e1000 with a real TCP/IP stack,
virtio-gpu and VBE framebuffers, and a device model where a driver never
names its consumer.
</p>
</div>
</li>
<li class="nv-steps__item" aria-current="step">
<span class="nv-steps__marker">5</span>
<div class="nv-steps__body">
<span class="nv-overline nv-text-tertiary">Now</span>
<h3 class="nv-steps__title">Drivers that are not in the kernel</h3>
<p class="nv-steps__text">
A process can be handed a PCI device — its registers, its memory, its
interrupts — and drive it. The GPU driver already runs that way. Nobody
writes a driver for every device that exists; the answer is to stop
requiring that they live inside.
</p>
</div>
</li>
</ol>
</section>
<!-- ====================================================================== -->
<section id="stack" class="shell section" data-reveal>
<div class="section__head">
<p class="nv-overline nv-text-accent">Architecture</p>
<h2 class="nv-heading">Three seams, and everything else is arrangement</h2>
<p class="nv-prose nv-text-secondary">
Above the syscall table, software is ordinary and portable. Below the device
model, a driver never names its consumer and a consumer never names a driver.
And at <code class="nv-code">/dev/pci</code>, a driver stops needing to be in
the kernel at all. Everything else is arrangement.
</p>
</div>
<div class="nv-card nv-card--flush stack">
<div class="stack__layer">
<span class="nv-label stack__name">userland</span>
<span class="nv-body nv-text-secondary">toybox, mksh, zsh, musl, and the first-party programs in <code class="nv-code">cmd/</code></span>
</div>
<div class="stack__layer">
<span class="nv-label stack__name">userspace drivers</span>
<span class="nv-body nv-text-secondary"><code class="nv-code">vgpud</code> — a driver that is an ordinary process</span>
</div>
<div class="stack__layer">
<span class="nv-label stack__name">schinit</span>
<span class="nv-body nv-text-secondary">PID 1 — job manifests, supervision, reincarnation</span>
</div>
<div class="stack__layer stack__layer--seam">
<span class="nv-label stack__name">syscall table<span class="nv-badge nv-badge--accent nv-badge--square">seam</span></span>
<span class="nv-body nv-text-secondary">Linux numbering, one <code class="nv-code">.inc</code> file — the ABI seam</span>
</div>
<div class="stack__layer">
<span class="nv-label stack__name">kernel services</span>
<span class="nv-body nv-text-secondary">VFS, sockets and <code class="nv-code">SCM_RIGHTS</code>, signals, mmap, poll, procfs</span>
</div>
<div class="stack__layer">
<span class="nv-label stack__name">subsystems</span>
<span class="nv-body nv-text-secondary">SchFS, lwIP, virtual terminals, graphics, the buffer cache</span>
</div>
<div class="stack__layer stack__layer--seam">
<span class="nv-label stack__name">device model<span class="nv-badge nv-badge--accent nv-badge--square">seam</span></span>
<span class="nv-body nv-text-secondary">class vtables and bus binding — the driver seam</span>
</div>
<div class="stack__layer stack__layer--seam">
<span class="nv-label stack__name">/dev/pci<span class="nv-badge nv-badge--accent nv-badge--square">seam</span></span>
<span class="nv-body nv-text-secondary">registers, DMA and interrupts to a process — the eviction seam</span>
</div>
<div class="stack__layer">
<span class="nv-label stack__name">drivers</span>
<span class="nv-body nv-text-secondary">xHCI, e1000, virtio-gpu, VBE, IDE, PS/2</span>
</div>
<div class="stack__layer">
<span class="nv-label stack__name">SchIFT core</span>
<span class="nv-body nv-text-secondary">scheduler, paging, traps, IRQ, SMP, DMA</span>
</div>
<div class="stack__layer">
<span class="nv-label stack__name">boot</span>
<span class="nv-body nv-text-secondary">the UEFI loader in <code class="nv-code">boot/uefi</code></span>
</div>
</div>
<p class="nv-body nv-text-tertiary section__note">
<code class="nv-code">uname -s</code> reports <b>SchIFT</b> — the kernel — the
way every Unix reports its kernel rather than its distribution.
</p>
</section>
<!-- ====================================================================== -->
<section id="what" class="shell section" data-reveal>
<div class="section__head">
<p class="nv-overline nv-text-accent">What it has</p>
<h2 class="nv-heading">Running today, and honest about the rest</h2>
<p class="nv-prose nv-text-secondary">
Every card marked <b>runs</b> is in the tree and works today. The tags are
not aspirational; the things that are not finished say so.
</p>
</div>
<!-- A rail rather than a grid. Eleven cards stacked into a wall is a list
you skim and stop reading; a row you move through is one you go along.
The section is a survey, not a comparison -- nothing here needs to be
read beside anything else, which is the test for whether a grid was
carrying information or just filling space. -->
<div class="nv-scroller-frame" data-nv-scroller>
<div class="nv-scroller features">
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><img src="Assets/uberblock.svg" alt=""></span>SchFS</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
Copy-on-write B-tree, a 128-slot uberblock ring, extents with refcounts
and reflink, CRC32c on metadata, and free space derived from the tree
rather than stored beside it — so there is no second structure to
disagree with the first. There is nothing to repair after a crash.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2"/><path d="M9 2v4M15 2v4M9 18v4M15 18v4M2 9h4M2 15h4M18 9h4M18 15h4"/></svg></span>SMP x86-64 kernel</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
Four-level paging, multiple cores, preemptive scheduling, a calibrated
LAPIC timer, real signal delivery and job control. Close to two
hundred syscalls on Linux numbering.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3.5 3 14.5 0 18M12 3c-3 3.5-3 14.5 0 18"/></svg></span>Networking</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
An e1000 driver under lwIP, with sockets for AF_INET, AF_UNIX and
AF_NETLINK. DHCP brings the interface up at boot;
<code class="nv-code">ping</code> reaches the outside world.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v8"/><path d="M6.3 6.3a8 8 0 1 0 11.4 0"/></svg></span>UEFI boot</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
A PE/COFF loader built with clang and lld-link that sets up its own page
tables and hands the kernel a framebuffer. No BIOS path, no chainloading.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 21V4M12 4l-2.5 3M12 4l2.5 3M12 16l-5-3V10M12 14l5-3V9"/><circle cx="7" cy="9" r="1.5"/><rect x="15.5" y="7" width="3" height="3"/></svg></span>USB</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
xHCI, hubs, HID and mass storage — a keyboard and a USB disk that the
kernel can mount, through the same device model as everything else.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 9l3 3-3 3M12 15h5"/></svg></span>Virtual terminals</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
Six of them, each with its own cell grid, scrollback and foreground
job — switched with <code class="nv-code">Ctrl+Alt+F1</code>…<code class="nv-code">F12</code>,
which is the one chord a Linux host does not take for itself. The console
draws to whichever framebuffer is present, from the kernel's first line.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="5" r="2"/><circle cx="6" cy="19" r="2"/><circle cx="18" cy="19" r="2"/><path d="M12 7v5M12 12l-6 5M12 12l6 5"/></svg></span>schinit</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
PID 1 does almost nothing: it forks a supervisor, spawns jobs on its
behalf, and reaps. The supervisor holds the manifests and the respawn
policy, and if it dies PID 1 replaces it and hands back the children it
was already watching. PID 1 can re-exec itself without dropping them.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9z"/><path d="M4 7.5l8 4.5 8-4.5M12 12v9"/></svg></span>A real userland</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
musl and toybox, both unmodified, plus mksh as <code class="nv-code">/bin/sh</code>.
<b>fastfetch</b> builds from upstream source with an empty
<code class="nv-code">patches/</code> directory — when it could not detect
something, the answer was to give the kernel what it was looking for.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="7" width="10" height="10" rx="1.5"/><path d="M6 4v3M10 4v3M6 17v3M10 17v3M16 12h5M18.5 9.5L21 12l-2.5 2.5"/></svg></span>Drivers outside the kernel</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
<code class="nv-code">/dev/pci</code> hands a process a device: its
registers by <code class="nv-code">mmap</code>, physically contiguous
memory it can point the device at, and interrupts as something to
<code class="nv-code">read()</code>. The virtio-gpu driver already lives
out there.
</div>
<div class="nv-card__footer nv-card__footer--plain">
<a class="nv-button nv-button--ghost nv-button--sm" href="#hybrid">How that works →</a>
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm" ><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="13" height="10" rx="1.5"/><rect x="8" y="10" width="13" height="10" rx="1.5"/></svg></span>The desktop</h3>
<span class="nv-badge nv-badge--success">runs</span>
</div>
<div class="nv-card__body nv-text-secondary">
Xorg on the framebuffer with Window Maker, <code class="nv-code">xterm</code> and a few X clients;
NetSurf for the web. No acceleration yet — the GPU is a plan, and the plan is Linux's own
<code class="nv-code">amdgpu</code> through LDLS.
</div>
</article>
<article class="nv-card">
<div class="nv-card__header">
<h3 class="nv-card__title"><span class="nv-tile nv-tile--sm" style="--nv-tile-tint: var(--nv-color-text-tertiary)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 4c-2 0-3 1-3 3v2c0 1.5-1 3-2 3 1 0 2 1.5 2 3v2c0 2 1 3 3 3M16 4c2 0 3 1 3 3v2c0 1.5 1 3 2 3-1 0-2 1.5-2 3v2c0 2-1 3-3 3"/></svg></span>SchAPI</h3>
<span class="nv-badge">designed</span>
</div>
<div class="nv-card__body nv-text-secondary">
The native programming interface — specified, not built, and deliberately
so. It should be designed <i>from</i> the desktop's experience rather
than before it.
</div>
</article>
</div>
<button class="nv-button nv-button--icon nv-button--float" data-nv-scroll="prev" aria-label="Previous">
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M14 6l-6 6 6 6" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<button class="nv-button nv-button--icon nv-button--float" data-nv-scroll="next" aria-label="Next">
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M10 6l6 6-6 6" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</section>
<!-- ====================================================================== -->
<section id="hybrid" class="shell section" data-reveal>
<div class="section__head">
<p class="nv-overline nv-text-accent">Hybrid</p>
<h2 class="nv-heading">A driver can leave the kernel</h2>
<p class="nv-prose nv-text-secondary">
Nobody writes a driver for every device that exists — not a project this
size, and not any project. So the interesting question is not how to write
more of them, but what a driver actually needs from a kernel, and whether it
can have that from outside.
</p>
</div>
<div class="needs">
<article class="nv-card nv-card--inset">
<div class="nv-card__header">
<div class="nv-stack nv-stack--tight">
<p class="nv-overline nv-text-tertiary">It needs</p>
<h3 class="nv-card__title">Its registers</h3>
</div>
</div>
<div class="nv-card__body nv-text-secondary">
<code class="nv-code">mmap</code> on <code class="nv-code">/dev/pci</code>,
where the offset is the physical address of a BAR. The kernel refuses any
offset that is not inside a BAR of a device this process was given, which
is what stops it being <code class="nv-code">/dev/mem</code> with extra steps.
</div>
</article>
<article class="nv-card nv-card--inset">
<div class="nv-card__header">
<div class="nv-stack nv-stack--tight">
<p class="nv-overline nv-text-tertiary">It needs</p>
<h3 class="nv-card__title">Memory the device can reach</h3>
</div>
</div>
<div class="nv-card__body nv-text-secondary">
Physically contiguous, and the process is told the physical address —
which is the whole reason it cannot do this for itself. The arena is
reserved at boot, before the general allocator gets anything.
</div>
</article>
<article class="nv-card nv-card--inset">
<div class="nv-card__header">
<div class="nv-stack nv-stack--tight">
<p class="nv-overline nv-text-tertiary">It needs</p>
<h3 class="nv-card__title">To be told when to look</h3>
</div>
</div>
<div class="nv-card__body nv-text-secondary">
Interrupts arrive by <code class="nv-code">read()</code>, so they compose
with <code class="nv-code">poll</code>. The kernel masks the line and
leaves it masked until the driver writes back — because a PCI interrupt is
level-triggered, and nobody but the driver knows how to clear it at the device.
</div>
</article>
<article class="nv-card nv-card--inset">
<div class="nv-card__header">
<div class="nv-stack nv-stack--tight">
<p class="nv-overline nv-text-tertiary">It needs</p>
<h3 class="nv-card__title">A descriptor to arrive</h3>
</div>
</div>
<div class="nv-card__body nv-text-secondary">
<code class="nv-code">sendmsg</code> with <code class="nv-code">SCM_RIGHTS</code>.
Handing a process the device it is allowed to touch and handing a
compositor a buffer turned out to be the same act, which is why one
primitive answered both.
</div>
</article>
</div>
<div class="nv-window console" aria-label="Evicting the GPU driver to userspace">
<div class="nv-window__titlebar">
<span class="nv-window__controls" aria-hidden="true">
<span class="nv-window__control nv-window__control--close"></span>
<span class="nv-window__control nv-window__control--minimize"></span>
<span class="nv-window__control nv-window__control--zoom"></span>
</span>
<span class="nv-window__title">vgpud — tty1</span>
</div>
<pre class="nv-window__body nv-code console__log"><span class="nv-text-tertiary">$ </span>vgpud
<span class="nv-text-accent">vgpud</span>: virtio-gpu at 00:03.0, currently held by virtio-gpu
<span class="nv-text-accent">virtio-gpu</span>: released to userspace; screen is the VBE framebuffer
<span class="nv-text-accent">vgpud</span>: claimed
<span class="nv-text-accent">vgpud</span>: display is 1280x800
<span class="nv-text-accent">vgpud</span>: scanout is ours, framebuffer at physical 0x3eceb000
<span class="nv-text-success">vgpud</span>: <span class="nv-text-success">mirroring the console</span>
</pre>
</div>
<p class="nv-body nv-text-tertiary section__note">
<b>No syscall in that transcript knows it is talking about a GPU.</b> The
kernel handed over a PCI device; the process decided what it was. That is
the property that makes the interface general — and the reason SchOS's own
drivers are being moved out through it first, rather than shaping it around
something foreign and hoping it fits.
</p>
</section>
<!-- ====================================================================== -->
<section id="numbers" class="shell section" data-reveal>
<div class="section__head">
<p class="nv-overline nv-text-accent">Numbers</p>
<h2 class="nv-heading">Measured, not marketed</h2>
</div>
<!-- stats:begin -->
<dl class="nv-stat-group">
<div class="nv-stat">
<dt class="nv-stat__label">lines of code in <code class="nv-code">kernel/</code> and <code class="nv-code">include/</code></dt>
<dd class="nv-stat__value">51,893</dd>
</div>
<div class="nv-stat">
<dt class="nv-stat__label">lines of comments alongside them</dt>
<dd class="nv-stat__value">17,213</dd>
</div>
<div class="nv-stat">
<dt class="nv-stat__label">first-party lines in all</dt>
<dd class="nv-stat__value">72,766</dd>
</div>
<div class="nv-stat">
<dt class="nv-stat__label">syscalls, Linux numbering</dt>
<dd class="nv-stat__value">193</dd>
</div>
<div class="nv-stat">
<dt class="nv-stat__label">drivers in the kernel</dt>
<dd class="nv-stat__value">15</dd>
</div>
<div class="nv-stat">
<dt class="nv-stat__label">Alpine packages in the live root</dt>
<dd class="nv-stat__value">179</dd>
</div>
<div class="nv-stat">
<dt class="nv-stat__label">commits</dt>
<dd class="nv-stat__value">292</dd>
</div>
<div class="nv-stat">
<dt class="nv-stat__label">days since the first one</dt>
<dd class="nv-stat__value">78</dd>
</div>
<div class="nv-stat">
<dt class="nv-stat__label">patches carried against fastfetch</dt>
<dd class="nv-stat__value">0</dd>
</div>
</dl>
<!-- stats:end -->
</section>
<!-- ======================================================================
PSOGM. The one place this purple is allowed, so it is spelled the one
way the system allows a subtree to carry its own colour: re-derive the
whole set from it, and let every role follow. -->
<section class="shell section" data-reveal>
<div class="nv-bed nv-bed--single crash" data-nv-brand style="--nv-brand: #4B1D9A">
<span class="nv-bed__label">When it stops</span>
<img class="crash__mark" src="Assets/shift.svg"
alt="Shift, the SchOS mark, as the crash screen draws it">
<div class="crash__copy">
<h2 class="nv-heading">PSOGM</h2>
<p class="nv-prose">
When the kernel panics it does not scroll a stack trace past you. It draws
a full-screen report — what failed, where, and the state that mattered —
and encodes the whole thing as a QR code so it can leave the machine
without being retyped. It reaches every display the machine has, including
the ones the kernel has handed to a userspace driver.
</p>
<p class="nv-prose nv-text-secondary">
This purple is <code class="nv-code">#4B1D9A</code>, and the theme reserves
it for exactly this. Normal states are calm and dark; only failure is loud.
It is the one colour in the system that means something, which is why it
appears once on this page and nowhere else.
</p>
</div>
</div>
</section>
<!-- ====================================================================== -->
<section id="wallpapers" class="shell section" data-reveal>
<div class="section__head">
<p class="nv-overline nv-text-accent">Wallpapers</p>
<h2 class="nv-heading">Four surfaces to sit on</h2>
<p class="nv-prose nv-text-secondary">
Every one is the Shift mark under a different light, generated from the
same geometry the loader draws — so they change when the mark does.
3840×2160, free to use under the same licence as the rest of the tree.
</p>
</div>
<div class="walls">
<figure class="nv-card wall">
<img class="wall__img" src="../branding/wallpapers/graphite.jpg" alt="Graphite: Shift on warm graphite, throwing a pool of violet on the surface. The default." loading="lazy" width="3840" height="2160">
<figcaption class="wall__caption">
<span class="nv-title">Graphite</span>
<span class="nv-body nv-text-secondary">Shift on warm graphite, throwing a pool of violet on the surface. The default.</span>
<span class="nv-cluster">
<a class="nv-button nv-button--sm" href="../branding/wallpapers/graphite.jpg" download>4K JPEG</a>
<a class="nv-button nv-button--ghost nv-button--sm" href="../branding/wallpapers/graphite.svg" download>SVG</a>
</span>
</figcaption>
</figure>
<figure class="nv-card wall">
<img class="wall__img" src="../branding/wallpapers/strata.jpg" alt="Strata: The slabs repeated into a cascade that recedes into indigo." loading="lazy" width="3840" height="2160">
<figcaption class="wall__caption">
<span class="nv-title">Strata</span>
<span class="nv-body nv-text-secondary">The slabs repeated into a cascade that recedes into indigo.</span>
<span class="nv-cluster">
<a class="nv-button nv-button--sm" href="../branding/wallpapers/strata.jpg" download>4K JPEG</a>
<a class="nv-button nv-button--ghost nv-button--sm" href="../branding/wallpapers/strata.svg" download>SVG</a>
</span>
</figcaption>
</figure>
<figure class="nv-card wall">
<img class="wall__img" src="../branding/wallpapers/porcelain.jpg" alt="Porcelain: The light one: glazed porcelain on paper, the top slab in the accent." loading="lazy" width="3840" height="2160">
<figcaption class="wall__caption">
<span class="nv-title">Porcelain</span>
<span class="nv-body nv-text-secondary">The light one: glazed porcelain on paper, the top slab in the accent.</span>
<span class="nv-cluster">
<a class="nv-button nv-button--sm" href="../branding/wallpapers/porcelain.jpg" download>4K JPEG</a>
<a class="nv-button nv-button--ghost nv-button--sm" href="../branding/wallpapers/porcelain.svg" download>SVG</a>
</span>
</figcaption>
</figure>
<figure class="nv-card wall">
<img class="wall__img" src="../branding/wallpapers/dial.jpg" alt="Dial: The SchFS uberblock ring as hardware — the newest slot and the root lit." loading="lazy" width="3840" height="2160">
<figcaption class="wall__caption">
<span class="nv-title">Dial</span>
<span class="nv-body nv-text-secondary">The SchFS uberblock ring as hardware — the newest slot and the root lit.</span>
<span class="nv-cluster">
<a class="nv-button nv-button--sm" href="../branding/wallpapers/dial.jpg" download>4K JPEG</a>
<a class="nv-button nv-button--ghost nv-button--sm" href="../branding/wallpapers/dial.svg" download>SVG</a>
</span>
</figcaption>
</figure>
</div>
</section>
<!-- ====================================================================== -->
<section id="docs" class="shell section" data-reveal>
<div class="section__head">
<p class="nv-overline nv-text-accent">Reading</p>
<h2 class="nv-heading">Where to look</h2>
<p class="nv-prose nv-text-secondary">
The documentation is written to outlive the work. Anything finished is
described in <code class="nv-code">architecture/</code>; anything intended
lives in <code class="nv-code">plan/</code>; and the bugs worth remembering
are kept in <code class="nv-code">history/</code>, in full, including the
wrong turns.
</p>
<p><a class="nv-button nv-button--primary" href="docs.html">Open the docs reader</a></p>
</div>
<div class="nv-card-grid">
<article class="nv-card nv-card--interactive">
<div class="nv-card__header">
<div>
<h3 class="nv-card__title">
<a class="nv-card__link" href="docs.html?doc=architecture/README.md">Architecture</a>
</h3>
<p class="nv-card__subtitle">What the system is, layer by layer</p>
</div>
</div>
</article>
<article class="nv-card nv-card--interactive">
<div class="nv-card__header">
<div>
<h3 class="nv-card__title">
<a class="nv-card__link" href="docs.html?doc=plan/1.0.md">The 1.0 definition</a>
</h3>
<p class="nv-card__subtitle">What is in, what is out, and why</p>
</div>
</div>
</article>
<article class="nv-card nv-card--interactive">
<div class="nv-card__header">
<div>
<h3 class="nv-card__title">
<a class="nv-card__link" href="docs.html?doc=history/kernel.md">Kernel history</a>
</h3>
<p class="nv-card__subtitle">The bugs, and what each one taught</p>
</div>
</div>
</article>
<article class="nv-card nv-card--interactive">
<div class="nv-card__header">
<div>
<h3 class="nv-card__title">
<a class="nv-card__link" href="docs.html?doc=guides/building.md">Building it</a>
</h3>
<p class="nv-card__subtitle">Toolchain, ports, and running under QEMU</p>
</div>
</div>
</article>
</div>
</section>
<!-- ====================================================================== -->
<section class="shell section" data-reveal>
<div class="nv-card nv-card--elevated getband">
<img class="getband__mark" src="Assets/usb.svg" alt="">
<div class="getband__copy">
<p class="nv-overline nv-text-accent">Try it</p>
<h2 class="nv-heading">One ISO. One stick. Your disks untouched.</h2>
<p class="nv-prose nv-text-secondary">
It runs from RAM and leaves the machine as it found it. Or skip the
stick entirely — <code class="nv-code">make qemu</code> is the same
system in a window.
</p>
<div class="nv-cluster">
<a class="nv-button nv-button--primary" href="download.html">Download SchOS</a>
<a class="nv-button" href="docs.html?doc=guides/building.md">Build it instead</a>
</div>
</div>
</div>
</section>
</main>
<footer class="shell site-footer">
<div class="nv-divider"></div>
<div class="site-footer__row">
<span class="nv-body">SchOS — built from the metal up. Kernel <b>SchIFT</b>, filesystem <b>SchFS</b>.</span>
<span class="nv-code nv-text-tertiary">GPL-2.0-or-later, with the xv6-derived core under MIT.</span>
</div>
<p class="nv-body nv-text-secondary pride-note" id="pride-note">
<img src="Assets/pride.svg" alt="">
Built by, and for, people of every orientation and gender identity.
The flag stays up all year.
</p>
<p class="nv-body nv-text-secondary nv-prose" id="ai-note">
<b class="nv-text-primary">On AI.</b>
Parts of SchOS were written with AI assistance, and that is not the measure
of anything. A project is good when the people building it understand what
was built, and bad when nobody does — whoever, or whatever, typed it. Vibe
your way through and you get something no one can fix. Stay in the loop and
you get a system you can explain line by line. The bug writeups in
<a href="docs.html?doc=history/kernel.md">history/</a> are where that gets
checked, including the wrong turns.
</p>
</footer>
<script src="app.js"></script>
</body>
</html>