SchOS/docs/plan/gpu.md
Nexoniarz cb3d28e3d6 docs: README, plan, guides and the site describe what is built now
README: rewritten around what runs (Linux software, Xorg, apk, Linux's drivers), the honest gaps, measured numbers.
plan/: 1.0.md is the finish line, where each clause of the acceptance test stands, and the register (one failing
clause: a root that survives a reboot); built plans (foundations, hybrid, alpine, xorg) and the decided-against one
(wayland) are gone, their content lives in architecture/; roadmap.md is only what is still open.
guides: building/layout/testing/adding-a-driver (Linux code may be used). history: four entries (ls -lR, TLB
shootdowns, MDIO 0x02460246, netlink MSG_TRUNC). Website: cards, stack, download page, docs index.
Code comments that pointed at deleted plan docs point at the architecture docs.

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

125 lines
6.6 KiB
Markdown
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.

# The GPU, through Linux's own driver
The laptop this is for (ASUS TUF A15 FA507NU) has a Radeon 680M in the CPU and
an RTX 4050 beside it. SchOS draws everything with the CPU on an EFI
framebuffer. Rendering a browser page that way keeps every core busy, and the
machine runs hot doing it.
Writing a Radeon driver is not on the table: amdgpu is around four million lines
before its register headers. The plan is to **run Linux's amdgpu unmodified**,
with its firmware, in a process under LDLS ([../architecture/ldls.md](../architecture/ldls.md)),
and to give Mesa the `/dev/dri` it expects. Licensing is not an obstacle:
SchOS is GPL-2.0-or-later, and the user has said that Linux code and firmware
blobs may be shipped. That supersedes LDLS's rule 1.
## Why LDLS and not an in-kernel linuxkpi
FreeBSD's drm-kmod compiles Linux's DRM source into kernel modules against a
shim (linuxkpi). SchOS has already chosen the other route, and for good reasons
([devices.md](../architecture/devices.md)): GPU drivers live outside the kernel, a crashed driver
falls back to the framebuffer instead of taking the machine down, and LDLS
loads unmodified `.ko` files against the running kernel's BTF instead of
needing a source port per Linux release. vgpud already drives a GPU from a
process, and LDLS already runs 8139too and e1000 that way.
## What it costs: the numbers
Measured against Linux 6.18.54 (the host's kernel and modules), with
`modprobe --show-depends`, `nm -u` and the export tables:
| Driver | Modules in the closure | Symbols needed from the core kernel | LDLS provides today |
|---|---|---|---|
| r8169 | 12 (libphy, mdio-bus, realtek PHY, ...) | 330 | 70 |
| amdgpu | 15 (ttm, gpu-sched, drm_buddy, drm_exec, drm_display_helper, ...) | 1,166 | 76 |
Of amdgpu's 1,166 symbols, **314 are the DRM core** (`drm_*`). NixOS builds
`drm.ko` and `drm_kms_helper.ko` into the kernel, so a module set that has them
as modules is needed; see step 0. A further 45 are dma-fence, dma-buf and
dma-resv. The rest is ordinary kernel: memory, locks, workqueues, timers, PCI,
ACPI, firmware loading, i2c, backlight.
## What LDLS lacks, and must grow
LDLS today assumes one thread, no-op locks and a polled interrupt handler
(ldls.md, "What it costs"). amdgpu cannot work under that:
1. **Threads.** `kthread_run`, workqueues with real worker threads,
`gpu-sched`'s scheduler threads. Map them to pthreads in the driver process.
2. **Locks that lock.** mutex, spinlock, rwsem, `ww_mutex` (TTM and dma-resv
depend on wound/wait semantics), completions and wait queues, on futexes.
3. **Interrupts as interrupts.** A thread blocked in `read()` on `/dev/pci`
calls the handler and any threaded handler, instead of the poll loop.
4. **Timers.** `timer_list`, `hrtimer`, `delayed_work` and `jiffies`, from a
timer thread.
5. **Memory with physical addresses.** TTM allocates system pages for GTT and
maps them for the GPU. Without an IOMMU, DMA address equals physical
address, so the `PCIIOC_DMA` arena has to grow to hundreds of megabytes,
with `struct page` (vmemmap) coverage for all of it.
6. **Firmware.** `request_firmware` reads `/lib/firmware/amdgpu/*.bin`, from
Alpine's `linux-firmware-amdgpu` package.
7. **ACPI.** On an APU the VBIOS comes from the VFCT table, and amdgpu calls
ATIF/ATCS. A small `/dev/acpi` (read a table, evaluate a method through
uACPI) covers both.
Each of these is general LDLS work, not amdgpu work. Every later driver needs
the same pieces: e1000e, iwlwifi, nouveau.
## The new kernel piece: `/dev/dri` served by a process
Mesa and Xorg open `/dev/dri/card0` and `/dev/dri/renderD128`, then use
`ioctl`, `mmap` and `poll`, and pass buffers to each other as dma-buf file
descriptors. The kernel has to forward each of those to the driver process:
- **open, ioctl, poll, close** go to the server process with the client's
identity attached, the same pattern `/dev/netdev` uses for NICs, extended to
ioctls with their argument buffers.
- **mmap** is the part the kernel must do itself. The server replies with
physical ranges (VRAM through the BAR, or GTT pages from the arena), and the
kernel maps those into the client's address space.
- **dma-buf** becomes a kernel file object naming a buffer the server owns.
`SCM_RIGHTS` passes it between processes (already built; see
[networking.md](../architecture/networking.md#descriptor-passing)), and X's DRI3 needs exactly that.
- **sysfs**: `/sys/class/drm/card0`, `/sys/dev/char/226:*`, and the
vendor/device files that libdrm and Mesa read to pick a driver.
## Order
Each step ends with something you can run.
0. **One module set.** Build Linux 6.18 from source, once, with `DRM=m`,
`DRM_AMDGPU=m` and BTF on. That gives `drm.ko`, `drm_kms_helper.ko` and the
rest, plus a matching `vmlinux` BTF. `make ldls-modules` does the build, and
packaging it as an apk makes it installable.
1. **LDLS concurrency and memory** (lacks 1–5 above), proven on **r8169**.
The real Linux r8169 brings PHY tuning, the firmware patch, ASPM handling
and every RTL8168 variant. It is the laptop NIC, so this step also fixes the
network properly. Done when the laptop's ethernet runs at gigabit speed
through `ldls0`.
2. **amdgpu probes.** The driver process loads firmware, brings up the IP
blocks and takes the display from the EFI framebuffer. The kernel console
must hand over the screen and get it back if the process dies. Done when
`dmesg` shows amdgpu's "SMU is initialized" and the panel stays lit.
3. **KMS.** `/dev/dri/card0` with ioctl forwarding and the mmap path. Done
when `modetest` (Alpine `libdrm-tests`) sets a mode and draws a frame.
4. **Rendering.** `renderD128`, GEM mmap, dma-buf, syncobj. Done when
`eglinfo` names radeonsi and `glmark2-es2-drm` and `vkcube` (RADV) run.
5. **The desktop.** Xorg's modesetting driver with glamor, then Epiphany with
GL compositing. Done when the fans stay quiet while a page scrolls.
## The RTX 4050
Later, and in two parts:
- **Powered off, now.** Without a driver the dGPU sits in D0 and draws power.
Putting it in D3cold through its ACPI power resources (`_PR3` / `_OFF`) is
a few dozen lines in the kernel and helps the heat and the battery today.
- **Driven, after amdgpu.** nouveau with GSP firmware (Ada needs it), through
the same LDLS and `/dev/dri` machinery, for PRIME render offload.
## Not GPU, but also why the laptop runs hot
Until now, idle CPUs halted at C1 and never reached the deep states listed in
ACPI `_CST`. That kept every core powered at an idle shell prompt. It is fixed
alongside this plan: the scheduler's idle path now enters `_CST`'s C2 (CC6 on
Zen). P-state control (AMD CPPC) is still missing, so the cores run at the
firmware's default clock under load.