docs: architecture describes what is built now
kernel (threads, demand paging, TLB shootdowns, dynamic linking, RAM direct map), syscalls, filesystem (/proc, /sys, /dev), terminals (12, VT handshake, ptys), networking (r8169 through LDLS), LDLS (module sets; Linux code may ship), devices (what stays in the kernel; hybrid.md's lasting content), boot (the loader's placement of the kernel), graphics (fbdev, GMMOS, QR 1-40), audio (ALSA over OSS), debugging, usb. New: packages.md (the apk root) and desktop.md (Xorg on the framebuffer). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
parent
655c7d8d01
commit
2556a955b3
15 changed files with 921 additions and 341 deletions
|
|
@ -1,8 +1,8 @@
|
|||
# Graphics, text and the console
|
||||
|
||||
Four things share this area and they are deliberately separate: the kernel's own
|
||||
drawing surface, the compositor's display interface, the text-rendering layer,
|
||||
and the crash screen.
|
||||
Five things share this area and they are deliberately separate: the kernel's own
|
||||
drawing surface, the `display` class a display server could use, the text-rendering
|
||||
layer, the crash screen — and `/dev/fb0`, which is how Xorg reaches the screen.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -13,11 +13,11 @@ first thing here.
|
|||
|
||||
| | [`gfx.h`](../../include/gfx.h) | [`display.h`](../../include/display.h) |
|
||||
|---|---|---|
|
||||
| **What** | The kernel's own drawing surface | The compositor's interface |
|
||||
| **What** | The kernel's own drawing surface | A frame interface for a display server |
|
||||
| **About** | Pixels and text | *Frames* |
|
||||
| **Operations** | `draw_pixel`, `fill_rect`, `putc`, `text_at` | `acquire`, `present`, `wait_vblank` |
|
||||
| **Buffering** | None. Direct writes to the scanout. | Double-buffered |
|
||||
| **Users** | The console and PSOGM | Schcom |
|
||||
| **Users** | The console, PSOGM, GMMOS | None today — Xorg draws through `/dev/fb0` |
|
||||
| **Must work when** | **Nothing else in the system does** | Userspace is healthy |
|
||||
|
||||
That last row is the reason they are not one interface. The console and the
|
||||
|
|
@ -67,7 +67,7 @@ and `vgpud` mirroring it see what is on the screen.
|
|||
`struct display_ops`, found with `device_find_class(DEVCLASS_DISPLAY)`.
|
||||
|
||||
`acquire()` allocates the back buffer **on first call, never at boot** — it is
|
||||
around two megabytes at 800×600, and a machine whose compositor never starts
|
||||
around two megabytes at 800×600, and a machine whose display server never starts
|
||||
should not pay that. A driver that cannot double-buffer returns 0, and the
|
||||
caller draws directly and accepts tearing rather than not drawing at all.
|
||||
`wait_vblank` is optional for the same reason.
|
||||
|
|
@ -133,7 +133,7 @@ A machine with both a stdvga and a virtio-gpu has two displays, and `vgpud` —
|
|||
the out-of-kernel driver, `cmd/vgpud/main.c` — owns the second one. For a long
|
||||
time it drew a test card there: colour bands, gradients, a checkerboard, and
|
||||
its own pid in binary along the bottom. That was scaffolding for
|
||||
[../plan/hybrid.md](../plan/hybrid.md)'s H2 and H3, where a screenshot had to
|
||||
the out-of-kernel driver work ([devices.md](devices.md)), where a screenshot had to
|
||||
answer *did a userspace process put this here* and *did the driver come back*.
|
||||
|
||||
Both of those are closed, so the test card became the Shift mark. Which was
|
||||
|
|
@ -223,9 +223,8 @@ shows the top-left corner, which is where a terminal puts everything worth
|
|||
seeing. Scaling is the alternative and it is the wrong one: text resampled to a
|
||||
non-integer ratio is text you cannot read.
|
||||
|
||||
**This is not the endpoint.** A compositor (Gate 3) will want this head for
|
||||
itself, and when there is one, the mirror goes away. Until then the honest
|
||||
thing for a working display to show is the system running on it.
|
||||
A display server that wants the head for itself can have it; until one does, the
|
||||
honest thing for a working display to show is the system running on it.
|
||||
|
||||
### The F8 resolution is the head's resolution
|
||||
|
||||
|
|
@ -263,6 +262,23 @@ not apply: two `vgpud`s cannot coexist, because the first holds the PCI claim
|
|||
and the second is refused the device long before it reaches `bind()`. The claim
|
||||
is the mutual exclusion; the socket node never was.
|
||||
|
||||
## /dev/fb0, and how Xorg draws
|
||||
|
||||
Xorg runs on the framebuffer through its `fbdev` driver, so `/dev/fb0` speaks Linux's
|
||||
fbdev: `FBIOGET_FSCREENINFO`, `FBIOGET_VSCREENINFO`, `FBIOPUT_VSCREENINFO` (accepted
|
||||
when it asks for the mode already in force), `FBIOPAN_DISPLAY`, `FBIOBLANK`,
|
||||
`FBIOPUTCMAP`, and `mmap` of the physical framebuffer. `FBIOGET_VSCREENINFO` reports
|
||||
nominal timings, as Linux's `efifb` does, instead of zeros — a zero pixel clock made
|
||||
fbdev compute a 0 Hz RandR mode. Pixel layout comes from the same table the kernel
|
||||
draws with, so a colour a program builds is the colour the kernel would.
|
||||
|
||||
Once a program maps the framebuffer the kernel has no way to know when it draws, so
|
||||
`fb_mapped()` marks the screen as no longer the text console's and `fbclose()` puts it
|
||||
back. A terminal switched to `KD_GRAPHICS` is not repainted ([terminals.md](terminals.md)),
|
||||
and the `VT_PROCESS` handshake tells the server before it loses the screen. `cmd/xready`
|
||||
checks every one of these calls before Xorg is started; the picture itself is the proof
|
||||
beyond that.
|
||||
|
||||
## Text
|
||||
|
||||
[`include/gfx/text.h`](../../include/text.h) + `kernel/dev/text.c`.
|
||||
|
|
@ -537,20 +553,59 @@ channel that works when the machine will not come back at all.
|
|||
machine whose network stack is dead**, and a phone camera is an out-of-band
|
||||
channel needing no driver on our side.
|
||||
|
||||
It encodes the **report itself**, not a URL — it reaches nothing. Deliberately
|
||||
small in three ways: byte mode only, EC level L only (the screen is a clean,
|
||||
evenly lit, high-contrast source; the damage QR's stronger levels protect
|
||||
against does not apply), and versions 1–6 only, stopping short of version 7's
|
||||
extra version-information blocks and version 10's wider count field.
|
||||
It encodes the **report itself**, not a URL — it reaches nothing. Byte mode only
|
||||
and EC level L only (the screen is a clean, evenly lit, high-contrast source; the
|
||||
damage QR's stronger levels protect against does not apply), but **every version,
|
||||
1 to 40**: version 40 at level L holds 2,953 bytes, which is what lets GMMOS put the
|
||||
kernel log on screen as a series of codes. Checked against the `segno` library module
|
||||
for module at every version.
|
||||
|
||||
134 bytes is a large crash report. If a payload ever needs more, the honest fix
|
||||
is to shorten the report.
|
||||
It draws on the report unconditionally rather than on a keypress, which is better:
|
||||
it costs nothing, and requiring input would mean the one case where input is least
|
||||
trustworthy is also the case where the report is hardest to retrieve. Verified by
|
||||
`make crashshot && make crashqr`, which decodes the symbol out of the captured
|
||||
framebuffer.
|
||||
|
||||
It draws unconditionally rather than on a keypress, which is better: it costs
|
||||
nothing, and requiring input would mean the one case where input is least
|
||||
trustworthy is also the case where the report is hardest to retrieve. Verified
|
||||
by `make crashshot && make crashqr`, which decodes the symbol out of the
|
||||
captured framebuffer.
|
||||
### Every processor stops
|
||||
|
||||
A panic used to stop only the processor it happened on; the rest carried on with a
|
||||
dead kernel, drew over the report, and kept the fans loud until somebody held the power
|
||||
button. The panicking processor now sends every other one an **NMI**, which arrives even
|
||||
with interrupts off, and `trap()` halts a processor that takes one while `panic_stop` is
|
||||
set — first thing, before the canary check, because that stack may be what broke. NMI has
|
||||
its own per-CPU stack (IST 1): for a few instructions at the top of `syscall_entry` the
|
||||
live stack is the user's, at ring 0, and a fault pushing the frame there would be a double
|
||||
fault on the same stack and a reset instead of a report. A second processor that panics
|
||||
while one is reporting halts.
|
||||
|
||||
## GMMOS — the Guru Meditation Micro OS
|
||||
|
||||
The report used to be the last thing the kernel did: a picture, blinking keyboard lights,
|
||||
and a machine that needed its power button. GMMOS keeps the report and goes on living
|
||||
under it (`kernel/gmmos/`, about 2,000 lines). The real kernel stays dead — nothing of it is
|
||||
called again that takes a lock, sleeps, allocates or waits for an interrupt. GMMOS has its
|
||||
own: the framebuffer, a clock read from the TSC, and keyboards it polls itself through
|
||||
**micro drivers**, cut-down copies of the real ones that do only what a keyboard needs.
|
||||
|
||||
| Key | |
|
||||
|---|---|
|
||||
| (nothing) | a status line counts down **three minutes** to power-off; any key cancels it |
|
||||
| **Space** | the last 300 lines of this boot's log as **QR codes**, a page per symbol, numbered so a phone can take them in order; `+` / `-` trade module size for capacity |
|
||||
| **L** | the log on screen, scrolled with the arrows |
|
||||
| **T** | a terminal: `why`, `log`, `grep`, `ps`, `cpus`, `pci`, `mem` (page tables walked first, so a bad address cannot fault), `uptime`, `restart`, `poweroff` |
|
||||
| **R R** / **P P** | restart / power off — twice, so a stray key does neither |
|
||||
|
||||
The micro drivers are **PS/2**, **I2C-HID** (the laptop's own keyboard, over a DesignWare
|
||||
controller — the live driver hands over what it found at boot, because AML cannot run on a
|
||||
dead kernel) and **xHCI** (USB keyboards: every controller is halted, reset and enumerated
|
||||
from nothing, root ports and USB 2 hubs up to two deep, a boot-protocol keyboard, no
|
||||
interrupts, a static 256 KB pool). GMMOS probes all of them and polls every one that answers.
|
||||
|
||||
Every buffer is static. A panic inside GMMOS, or a second one in the report, is reported
|
||||
and then only blinks; GMMOS does not re-enter itself. Checked under QEMU: every screen, two
|
||||
QR pages read off a screenshot with `zbar` and decoded to the log text exactly, restart
|
||||
reboots, and left alone it powers off at three minutes. What has been seen on real hardware
|
||||
is the report and the keyboards the laptops have; the xHCI path is the least exercised.
|
||||
|
||||
## One palette
|
||||
|
||||
|
|
@ -571,16 +626,20 @@ makes it mean something.
|
|||
|
||||
## What it does not do
|
||||
|
||||
- **The console still has its cell grid.** Cells are now a Unicode codepoint
|
||||
drawn by the TrueType rasteriser rather than a CP437 byte drawn from a bitmap,
|
||||
and the cell size follows the font's measured advance — but it is still a
|
||||
fixed grid, and the text layer's proportional path is unused by it.
|
||||
- **No mode setting.** Resolution is whatever the loader asked for. There is no
|
||||
`/dev/dri`-style device and no way to change it at runtime.
|
||||
- **No double buffering in practice.** `display_ops.acquire` is specified and
|
||||
the compositor needs it; virtio-gpu binds but exposes no modeset API, and
|
||||
- **The console still has its cell grid.** Cells are a Unicode codepoint drawn by the
|
||||
TrueType rasteriser, and the cell size follows the font's measured advance (the font
|
||||
scales to about 45 rows, 16 to 40 px per em, on whatever mode the firmware booted
|
||||
in) — but it is a fixed grid, and the text layer's proportional path is unused by it.
|
||||
- **Mode setting is the loader's and `vgpud`'s.** On real hardware resolution is whatever
|
||||
the loader chose from its menu — which now asks "Keep this resolution?" and falls back
|
||||
by itself, because AMD's GOP "sets" smaller modes without leaving the panel's native
|
||||
timing and the screen showed three or four squashed copies side by side. There is no
|
||||
`/dev/dri`, no KMS, and Xorg runs at the mode the console is in.
|
||||
- **No double buffering in practice.** `display_ops.acquire` is specified and nothing
|
||||
uses it; virtio-gpu exposes a modeset API on `vgpud`'s socket and nothing more, and
|
||||
`wait_vblank` is unimplemented everywhere.
|
||||
- **No hardware acceleration of any kind**, and none is planned before 2D blit.
|
||||
- **No hardware acceleration of any kind**: Xorg draws in software to the framebuffer
|
||||
(no glamor, no DRI), and GPU drivers are a plan, not a feature ([../plan/gpu.md](../plan/gpu.md)).
|
||||
- **No CFF/Type 2 charstrings**, so OTF fonts do not render — that is a separate
|
||||
`font_ops` provider and a bytecode interpreter, not an extension of the `glyf`
|
||||
path. TrueType hinting is deliberately out of scope.
|
||||
|
|
@ -593,6 +652,6 @@ makes it mean something.
|
|||
- **No composite point-matching.** Components positioned by matching a point in
|
||||
the base rather than by an offset are placed at the origin. Fonts that use it
|
||||
are rare and none in the tree do.
|
||||
- **PSOGM renders and stops.** The interactive crash mode described in
|
||||
[../plan/roadmap.md](../plan/roadmap.md) — walk the stack, read another
|
||||
process's memory, dump page tables — is designed and not built.
|
||||
- **GMMOS cannot look at a live process by name.** The "interactive crash mode" is built as far
|
||||
as a dead machine allows — log, a terminal, `ps`, `cpus`, `pci`, memory by physical walk — but
|
||||
the scheduler is gone, so there is no running a debugger against a process.
|
||||
|
|
|
|||
Loading…
Reference in a new issue