include: one folder per subsystem, and includes that name it

Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-23 15:05:12 +02:00
commit a1c965da1e
267 changed files with 1560 additions and 1440 deletions

View file

@ -17,7 +17,7 @@
It is marked twice over: it carries the accent, the same colour as the root at
the centre, and it reaches outward past the ring while sharing the inner edge
with every other slot. The colour is the reading on screen; the size is what
survives include/logo.h, which is a single coverage channel and keeps no
survives include/gfx/logo.h, which is a single coverage channel and keeps no
colour at all.
Stands alone, with no reference to SchOS, as projects/schfs-linux/README.md

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before After
Before After

View file

@ -30,7 +30,7 @@ is a token. A hex value in that file means the system is missing something, and
the answer is to add it to Niveus rather than to work around it here.
What that replaced was a 21 KB `style.css` that transcribed
[`include/theme.h`](../../include/theme.h) by hand — palette, type scale,
[`include/gfx/theme.h`](../../include/theme.h) by hand — palette, type scale,
shadows, the lot. The intent was right and the mechanism was a copy: two files
that had to be kept in step by remembering to. Now the whole identity is one
declaration, in the `<head>` of each page:

View file

@ -9,7 +9,7 @@
answer is to add one rather than to work around it.
The one exception is `--nv-brand`, which is declared in the pages: it is
SCH_ACCENT from include/theme.h, and it is an input to the system rather
SCH_ACCENT from include/gfx/theme.h, and it is an input to the system rather
than a restatement of it.
========================================================================== */

View file

@ -26,7 +26,7 @@
</script>
<!-- The whole identity, and it is not a design decision: SCH_ACCENT, copied
from include/theme.h. A site describing an operating system should look
from include/gfx/theme.h. A site describing an operating system should look
like the operating system. -->
<style>
:root { --nv-brand: #7C5CFF; }

View file

@ -44,7 +44,7 @@ because there are none.** Everything the jump needs is computed before.
### The hand-off block
One page of low memory at `0x30000`, defined once in
[`include/schboot.h`](../../include/schboot.h) and included by both the loader
[`include/arch/schboot.h`](../../include/schboot.h) and included by both the loader
and `entry.S` so the two cannot disagree.
| Offset | What |
@ -463,7 +463,7 @@ what makes a loop over candidates legal at all.
| 6 | IDE 0 | usually the ESP, so usually not SchFS |
The **registered** devices are the ones found at probe time rather than known
at build time, numbered from 4 (`include/blkdev.h`): a SATA disk behind AHCI
at build time, numbered from 4 (`include/fs/blkdev.h`): a SATA disk behind AHCI
(`sata0`), an NVMe namespace (`nvme0n1`), each USB stick (`usb0`..`usb3`, the
first of them also seen as device 3), and every partition on any of them
(`sata0p2`, `nvme0n1p2`, `usb0p2`). When a disk registers, its GPT is read, or its MBR

View file

@ -11,7 +11,7 @@ Moving drivers out of the kernel entirely is [../plan/hybrid.md](../plan/hybrid.
## One list of everything attached
There is one list of everything attached, whatever bus found it:
`include/device.h` and `kernel/dev/device.c`, readable at `/proc/devices`.
`include/dev/device.h` and `kernel/dev/device.c`, readable at `/proc/devices`.
```
name bus class driver device

View file

@ -344,7 +344,7 @@ both need the `..` fixup and the ancestor-loop check that are not written.
A name is at most 255 bytes and a path 4096 including its NUL, Linux's
`NAME_MAX` and `PATH_MAX` (`VFS_NAME_MAX` and `VFS_PATH_MAX` in
`include/file.h`). Longer is `ENAMETOOLONG`. A filesystem may hold less: the v1
`include/fs/file.h`). Longer is `ENAMETOOLONG`. A filesystem may hold less: the v1
format stores `DIRSIZ` bytes and refuses longer names rather than matching on
their first 30.

View file

@ -265,7 +265,7 @@ is the mutual exclusion; the socket node never was.
## Text
[`include/text.h`](../../include/text.h) + `kernel/dev/text.c`.
[`include/gfx/text.h`](../../include/text.h) + `kernel/dev/text.c`.
**The font was never the problem — the character-cell grid was.** The console
draws text by indexing `font_data[256][16]` and stepping the cursor eight
@ -554,7 +554,7 @@ captured framebuffer.
## One palette
[`include/theme.h`](../../include/theme.h). There were three unrelated designs
[`include/gfx/theme.h`](../../include/theme.h). There were three unrelated designs
and the seams showed: the splash was deep purple with gold text deliberately
matching the *panic* screen, the console was cream with near-black text, and so
a healthy boot was dressed as a crash and then abruptly became an amber

View file

@ -61,7 +61,7 @@ one's bug.
### The protocol
`include/schinit.h`. **Every message is exactly one struct**, and PID 1 reads
`include/uapi/schinit.h`. **Every message is exactly one struct**, and PID 1 reads
`sizeof(struct)` or nothing: no length prefix to get wrong, no partial-message
state machine, and no allocation in the process that must not fail. The socket is
a `socketpair` inherited as fd 3.

View file

@ -108,7 +108,7 @@ one per-CPU block and everything that changes per CPU belongs in it.
> calls. But `%fs` here is per *CPU*, and a kernel thread changes CPU at every
> `sched()`. So the assumption is false for exactly the variables that most need
> to be right, and the failure is a null dereference deep in the scheduler on any
> machine with more than one core. `include/proc.h` carries the disassembly that
> machine with more than one core. `include/core/proc.h` carries the disassembly that
> proves it.
## Memory
@ -119,7 +119,7 @@ no buddy system, no zones, no page-frame database.
### Small objects
`kernel/core/kmem.c`, `include/kmem.h`. A slab is one page from `kalloc()` with
`kernel/core/kmem.c`, `include/core/kmem.h`. A slab is one page from `kalloc()` with
a header at the front and objects packed after it; free objects hold the free
list in their own first eight bytes.
@ -196,7 +196,7 @@ contiguous multi-page allocator, which is a separate thing and is not written.
## Processes
`struct proc` in [`include/proc.h`](../../include/proc.h). A fixed table of
`struct proc` in [`include/core/proc.h`](../../include/proc.h). A fixed table of
`NPROC` entries, allocated by linear scan.
**The field order at the top of the struct is fixed by assembly.** `trapasm.S`

View file

@ -334,7 +334,7 @@ format bugs are found on the host where a test takes a second instead of a boot.
## The source
```
include/schfs.h on-disk format + compile-time layout assertions
include/fs/schfs.h on-disk format + compile-time layout assertions
kernel/fs/schfs/btree.{c,h} the copy-on-write B-tree and its cursor
kernel/fs/schfs/schfs_int.h volume + object layer interface
kernel/fs/schfs/space.c allocation bitmap, derived from the tree at mount
@ -345,7 +345,7 @@ kernel/fs/schfs/kmount.c kernel: block device, mount slots, transaction brac
kernel/fs/schfs/vfs.c the fs_ops table: SchFS behind open/read/write
tools/mkschfs.c build an image from a directory tree
tools/schfs_fsck.c verify, list, extract
include/fsstat.h counters behind /proc/fsstat: where the time goes
include/fs/fsstat.h counters behind /proc/fsstat: where the time goes
tools/schfs_test.c B-tree torture harness
tools/schfs_objtest.c object layer: files, directories, space accounting
tools/posix/schfs_roundtrip.sh end-to-end filesystem test

View file

@ -20,7 +20,7 @@ hand-maintained:
| Generated | Where |
|---|---|
| The `SYS_*` constants | `include/syscall.h` |
| The `SYS_*` constants | `include/core/syscall.h` |
| The dispatch table | `kernel/sys/syscall.c` |
| The Linux translation map | `kernel/sys/linux_abi.c` |

View file

@ -55,7 +55,7 @@ Eight bytes:
| `pad` | to eight, so the grid indexes without a multiply |
`ch` was one byte holding a CP437 glyph, not a codepoint: the console mapped
UTF-8 down to CP437 on the way in, because that is what `include/font.h`
UTF-8 down to CP437 on the way in, because that is what `include/gfx/font.h`
contained. Widening it to the codepoint is what made Ą, Ę, Ś, Ω and the rest
possible, and no font change could have done it alone — a byte cannot *hold*
U+0104 whatever is drawing it. Mapping on the way in also meant the decision
@ -249,7 +249,7 @@ terminals behind your back. Right Alt is tracked as its own bit (`RALT`) for
the same reason; it used to share `ALT`, which is harmless for typing — neither
is used to look up a character — and wrong for chords.
`include/posix.h`'s `VTC_*` bits mirror `include/kbd.h`'s modifier bits, and a
`include/uapi/posix.h`'s `VTC_*` bits mirror `include/dev/kbd.h`'s modifier bits, and a
`_Static_assert` in `kbd.c` holds them together. A mismatch would be silent:
a chord set from userspace would simply never match, and the only symptom
would be a key that does nothing.

View file

@ -27,7 +27,7 @@ Linux's drivers are GPL-2.0-only and are not — see
## What the spec gives you for free
Everything in [`include/usb.h`](../../include/usb.h) comes from USB 2.0
Everything in [`include/dev/usb.h`](../../include/usb.h) comes from USB 2.0
chapter 9, which every device implements regardless of speed, class or vendor.
**That is what makes enumeration possible at all:** before you know anything

View file

@ -30,8 +30,8 @@ a brief for one is not worth carrying until somebody is going to draw it.
## The one constraint everything answers to
**The kernel draws its mark as a 128×128 single-channel coverage mask.**
`include/logo.h` holds no colour at all — two pixels per byte, sixteen levels of
ink — and the kernel composites it in whatever `include/theme.h` says.
`include/gfx/logo.h` holds no colour at all — two pixels per byte, sixteen levels of
ink — and the kernel composites it in whatever `include/gfx/theme.h` says.
So any mark that will end up there has to survive as **a solid shape in one
colour at 128 px, and be recognisable at 24**. That rules out hairlines, thin
@ -64,7 +64,7 @@ the kernel's name (**SchIFT**) as a shape.
## Colour
From `include/theme.h`, which is the source of truth. Nothing here restates a
From `include/gfx/theme.h`, which is the source of truth. Nothing here restates a
value it does not already define.
| | | |
@ -128,7 +128,7 @@ superseded-*.png the old wordmark, kept for reference only
The pipeline is three stages:
```
shift-mask.svg --(inkscape)--> shift-mask.png --(make logo)--> include/logo.h
shift-mask.svg --(inkscape)--> shift-mask.png --(make logo)--> include/gfx/logo.h
```
**Both intermediates are committed, and that is deliberate.** `inkscape` is not
@ -154,7 +154,7 @@ If either is edited, edit both.
| | |
|---|---|
| Loader splash | Shift, from `include/logo.h`, shaded per row by `metal()` in `boot/common/ui.c` — silver, not accent. |
| Loader splash | Shift, from `include/gfx/logo.h`, shaded per row by `metal()` in `boot/common/ui.c` — silver, not accent. |
| Kernel console / early boot | Shift, same mask, composited flat in a theme colour by `gfx_draw_logo`. |
| A display with no compositor | Shift again, drawn by `vgpud` on `SCH_BG`. It replaced a test card — colour bands, gradients, a checkerboard and the driver's pid in binary — which was scaffolding for [../plan/hybrid.md](../plan/hybrid.md)'s H2 and H3 and outlived them. The mark answers the same question a black screen cannot, *something is driving this device*, and answers it as the system rather than as a test rig. |
| PSOGM | Shift, the same mask again, by `gfx_draw_logo()` from `kernel/core/panic.c`. At its native 128 px — there is no resampler in the crash path, and the mark was only ever drawn at one size there anyway. |

View file

@ -3,7 +3,7 @@
<desc>SAME GEOMETRY AS "shift.svg". Only the fills differ, and they are
greys because they are not colours: tools/python/mklogo.py reads ink as
(255 - luminance) * alpha, so what is stored is coverage. The kernel then
composites that coverage in whatever include/theme.h says the accent is.
composites that coverage in whatever include/gfx/theme.h says the accent is.
The greys run DARK AT THE TOP because the relationship inverts: darkest here
is most ink, which the kernel draws brightest. Copying the violet fills from

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

@ -17,7 +17,7 @@
It is marked twice over: it carries the accent, the same colour as the root at
the centre, and it reaches outward past the ring while sharing the inner edge
with every other slot. The colour is the reading on screen; the size is what
survives include/logo.h, which is a single coverage channel and keeps no
survives include/gfx/logo.h, which is a single coverage channel and keeps no
colour at all.
Stands alone, with no reference to SchOS, as projects/schfs-linux/README.md

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before After
Before After

View file

@ -15,7 +15,8 @@ schos/
├─ boot/ uefi/ the PE/COFF loader (splash, F8 menu, kernel load),
│ common/ the UI it shares. UEFI only -- there is no BIOS path.
├─ kernel/ The kernel, by subsystem: arch/ core/ dev/ fs/ proc/ sys/
├─ include/ Headers shared by kernel, userland, and tools/mkfs
├─ include/ Headers shared by kernel, userland, and tools/mkfs, by
│ subsystem -- see "include/" below
├─ lib/ First-party userland runtime: _start glue, syscall stubs,
│ the initcode blob embedded in the kernel, user.ld
├─ cmd/ First-party programs — one directory per program:
@ -57,6 +58,32 @@ schos/
└─ build/ ALL generated output (see below)
```
## include/
One folder per subsystem, and every include names it: `#include
"core/proc.h"`, not `"proc.h"`. The compiler is given `-I./include` and
nothing deeper, so the folder is the only way to reach a header and the
include line says where to find it.
| folder | what is in it |
|---------|---------------|
| `arch/` | the x86-64 machine: `mmu.h`, `x86.h`, `traps.h`, `memlayout.h`, `mp.h`, `ioperm.h`, `vmap.h`, `asm.h`, and `schboot.h`, the loader's handoff block |
| `core/` | the kernel proper: `types.h`, `param.h`, `defs.h`, `proc.h`, the locks, `kmem.h`, `dma.h`, `irq.h`, `klog.h`, `dbg.h`, `sbuf.h`, `syscall.h`, `uaccess.h`, `hwinfo.h`, `elf.h`, `date.h` |
| `fs/` | the VFS and what is under it: `fs.h`, `file.h`, `buf.h`, `blkdev.h`, `schfs.h`, `fat.h`, `fsstat.h` |
| `dev/` | devices and buses: `device.h`, `pci.h`, `pciuser.h`, `usb.h`, `virtio.h`, `acpi.h`, `input.h`, `kbd.h`, `keymap.h`, `vt.h`, `audio.h`, `netdev.h` |
| `gfx/` | pixels: `display.h`, `fb.h`, `gfx.h`, `font.h`, `text.h`, `ttf.h`, `theme.h`, `logo.h` (generated), `qr.h` |
| `net/` | `socket.h`, `netuser.h` |
| `uapi/` | what userland is built against: `user.h`, `posix.h`, `errno.h`, `stat.h`, `fcntl.h`, `schinit.h` |
| `ldls/` | the Linux driver layer's headers |
A side effect worth having: `errno.h`, `stat.h` and `fcntl.h` are no longer
at the top of `include/`, so a host tool built with `-I./include` gets the
system's versions of them rather than ours.
`tools/fix-includes.py` rewrites a bare `#include "proc.h"` to the folder the
header is in, reading the folders from the tree. Run it with `--modified` on
code written before the split, or after moving a header.
## build/
```

View file

@ -61,7 +61,7 @@ Fixed:
it's touched, with a clear message.
- No TLB invalidation existed anywhere except the full `lcr3` reload on
context switch — `mappages()`/`vm_unmap_range()` never called `invlpg`.
Fixed by adding `invlpg()` (`include/x86.h`) to both functions.
Fixed by adding `invlpg()` (`include/arch/x86.h`) to both functions.
- **`MSR_STAR` was misconfigured** (`syscallinit()`, `kernel/arch/x86_64/vm.c`):
`SYSRETQ` computes the returned `cs`/`ss` as `STAR[63:48]+16`/`+8`, which
requires the *compat* 32-bit code selector (`SEG_UCODE32`) in that field —
@ -76,7 +76,7 @@ Fixed:
length. A single allocation over 8MB (e.g. `realloc()` growth past that
size) mapped straight into a neighboring live mapping and hit
`mappages()`'s `panic("remap")`. Replaced with a real bump allocator
(`proc->mmap_next`, `include/proc.h`), so every mapping gets its own
(`proc->mmap_next`, `include/core/proc.h`), so every mapping gets its own
disjoint, exactly-sized range regardless of size.
- **`uva2ka()`** (`kernel/arch/x86_64/vm.c`) returned only the page-aligned
base address of a user pointer's page, silently dropping the in-page byte
@ -105,7 +105,7 @@ Bringing in toybox (see "Coreutils" below) surfaced four more, all fixed:
- `sys_open`/`sys_openat` required `omode == O_RDONLY` *exactly* to open a
directory, but `opendir()`-style calls legitimately OR in `O_DIRECTORY`/
`O_CLOEXEC` alongside it. Fixed to check just the access-mode bits
(`O_ACCMODE`, added to `include/fcntl.h`).
(`O_ACCMODE`, added to `include/uapi/fcntl.h`).
- `sys_getdents64` read one `struct dirent` (16 bytes) at a time — a full
`bmap()`+`bread()` per slot. Directories here are hash-indexed (see
`tree_search()`/`tree_expand()` below), so a directory with a few dozen
@ -207,7 +207,7 @@ independently re-reads mode fields from the same block.
Splash/progress bar/boot menu rendering is a small standalone pixel
renderer in `stage2main.c` (not the kernel's `vga.c` — paging isn't set up
yet, so the LFB physical address from the VBE block is used as a plain
pointer) reusing the kernel's own font data (`include/font.h`) directly.
pointer) reusing the kernel's own font data (`include/gfx/font.h`) directly.
It fills the framebuffer with PSOGM's purple (matching
`kernel/core/console.c`'s panic screen) and draws "SCHOS" centered, then:
@ -359,7 +359,7 @@ one tick and returned `-EINTR` unconditionally. mksh's wait loop had no
way to ever learn the child had exited, hanging forever.
Real signal delivery was implemented from scratch:
- `struct proc` (`include/proc.h`) gained `sig_handler[32]`/
- `struct proc` (`include/core/proc.h`) gained `sig_handler[32]`/
`sig_restorer[32]` (populated by `sys_rt_sigaction()`,
`kernel/sys/sys_sig.c`), `saved_sigtf` (stashed pre-signal trapframe),
and `in_signal` (re-entrancy guard). `allocproc()` zeroes these for a
@ -682,7 +682,7 @@ boot rather than being wiped a few hundred milliseconds in.
**There were three unrelated designs.** The splash was deep purple with gold
text, deliberately matching the *panic* screen. The console was cream with
near-black text. So a healthy boot was dressed as a crash, then abruptly
became an amber terminal. `include/theme.h` is now the one palette, and the
became an amber terminal. `include/gfx/theme.h` is now the one palette, and the
rule is: **normal states are calm and dark; only failure is loud.** PSOGM keeps
the purple entirely to itself, which is what makes it mean something.

View file

@ -74,7 +74,7 @@ ls /mnt ; echo hi > /mnt/tmp/w ; cat /mnt/tmp/w ; mkdir /mnt/tmp/d
```
The VFS grew a real dispatch layer rather than a set of `if (dev == SCHFS_DEV)`
checks. `struct fs_ops` (include/file.h) is one table per filesystem, chosen by
checks. `struct fs_ops` (include/fs/file.h) is one table per filesystem, chosen by
`iget()` from the device number and hung off the inode; v1's implementations
keep their bodies and are simply named `v1_*`. Adding a third filesystem is a
new table and nothing else. `kernel/fs/schfs/vfs.c` is SchFS's, and it is pure
@ -100,7 +100,7 @@ Three things had to change on the way, none of them SchFS's fault:
Three long-standing bugs surfaced the moment real commands ran against it, all
in the shared VFS rather than in SchFS:
- `include/fcntl.h` used xv6's open flags while every program in the userland
- `include/uapi/fcntl.h` used xv6's open flags while every program in the userland
sends Linux's. `O_APPEND` (0x400) landed on `O_TRUNC`, so `cmd >> file`
*truncated* it; `O_TRUNC` (0x200) landed on `O_CREATE`, so `cmd > file` left
whatever was past the new end in place. Nothing about either pointed at a
@ -273,7 +273,7 @@ because asking a filesystem a question does not.
Not starting from zero:
- **`struct schfs_extent`** is already defined in `include/fs.h` and unused.
- **`struct schfs_extent`** is already defined in `include/fs/fs.h` and unused.
The extent concept is already sketched; v2 gives it refcounts and 64-bit
fields.
- **CRC32c checksum fields** already exist in `dinode` and `superblock`, and

View file

@ -256,7 +256,7 @@ These are the galling ones — the replacement exists, is tested, and is unused.
rest of it retired — which needs explaining, because the item as written
asked for something a terminal cannot be.
`include/text.h` made the case against the grid in four charges: it
`include/gfx/text.h` made the case against the grid in four charges: it
cannot do variable-width glyphs, cannot position text off a multiple of
eight, cannot anti-alias, and cannot hold a codepoint above 255. **All
four were about the drawing layer, and all four are now false** — glyphs

View file

@ -45,7 +45,7 @@ the one that survives contact with a real machine.
## Built so far
**Mechanisms 2 and 4 exist.** `/dev/pci` (include/pciuser.h, kernel/dev/pciuser.c)
**Mechanisms 2 and 4 exist.** `/dev/pci` (include/dev/pciuser.h, kernel/dev/pciuser.c)
enumerates the bus, hands a device to a process exclusively, maps a BAR into it
and allocates memory a device can be pointed at. `pcitest` proves it end to end:
@ -378,7 +378,7 @@ a panic, so the process holding the device is never going to run again, and the
kernel let go of the device deliberately.
**So the userspace driver says in advance where it is scanning out**, with
`PCIIOC_PANICFB` (see `include/pciuser.h`), and the kernel does the rest:
`PCIIOC_PANICFB` (see `include/dev/pciuser.h`), and the kernel does the rest:
1. paints the report into that memory,
2. asks the *kernel* driver the device was taken from to put it on screen —