The README led with a wallpaper and described the system of two months ago: no desktop, no dynamic linking, a handful of drivers. It now leads with Shift and covers Xorg and Window Maker, apk and Alpine packages, SCFF, HTTPS, the storage, network and input drivers, and the machines it has booted on. "Not there yet" lists what is actually missing today: threads, udev, EHCI, touchpad gestures, an installer. The numbers -- lines of code and of comments, syscalls, drivers, packages, commits -- come from tools/python/stats.py, which rewrites them in the README and on the website between stats markers instead of somebody typing them once and letting them go stale. .gitattributes keeps the generated font and logo tables and the vendored Niveus out of the repository page's language bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
786 lines
38 KiB
HTML
786 lines
38 KiB
HTML
<!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">AltGr+F1</code>…<code class="nv-code">F6</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" style="--nv-tile-tint: var(--nv-color-warning)"><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>Schcom</h3>
|
||
<span class="nv-badge nv-badge--warning">in progress</span>
|
||
</div>
|
||
<div class="nv-card__body nv-text-secondary">
|
||
The compositor that will own the display. v0 runs, and descriptor
|
||
passing — the one thing a Wayland client cannot do without — now
|
||
exists. What a buffer still needs is a shareable memory object.
|
||
</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 compositor'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">45,207</dd>
|
||
</div>
|
||
<div class="nv-stat">
|
||
<dt class="nv-stat__label">lines of comments alongside them</dt>
|
||
<dd class="nv-stat__value">16,111</dd>
|
||
</div>
|
||
<div class="nv-stat">
|
||
<dt class="nv-stat__label">first-party lines in all</dt>
|
||
<dd class="nv-stat__value">61,367</dd>
|
||
</div>
|
||
<div class="nv-stat">
|
||
<dt class="nv-stat__label">syscalls, Linux numbering</dt>
|
||
<dd class="nv-stat__value">184</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">88</dd>
|
||
</div>
|
||
<div class="nv-stat">
|
||
<dt class="nv-stat__label">commits</dt>
|
||
<dd class="nv-stat__value">253</dd>
|
||
</div>
|
||
<div class="nv-stat">
|
||
<dt class="nv-stat__label">days since the first one</dt>
|
||
<dd class="nv-stat__value">76</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>
|