SchOS/.claude/memory/schunix-virtio-gpu.md
Nexoniarz a1c965da1e 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
2026-10-23 15:05:12 +02:00

3 KiB

name description metadata
schunix-virtio-gpu virtio-gpu 2D driver — the display class's second implementation; how to run it and the transport details that matter.
node_type type originSessionId modified
memory project 9d8a2aef-bcf0-47a0-96c6-f706efab4a95 2026-08-08T19:59:27.522Z

Written 2026-08-08 from the OASIS virtio 1.1 specification (chapters 2, 4.1, 5.7). include/dev/virtio.h + kernel/drivers/virtio_gpu.c. Worked on the first boot.

virtio-gpu: 1280x800, 4000 KB framebuffer, queue 64
display: 1280x800 32bpp stride 5120
display: presented a full frame

Run it with: -vga std -device virtio-gpu-pci. Keep -vga std — the bootloader draws the splash through VBE and there is no virtio path that early, so a machine with only virtio-gpu boots blind until the kernel is up. Both register as DEVCLASS_DISPLAY; registry.c lists virtio-gpu first so device_find_class() returns it, and vga_pci still claims the stdvga card underneath for the splash and text console.

Why it matters over VBE: presenting is explicit. VBE is a flat aperture with no notion of a finished frame, so a compositor races the scanout. Here you TRANSFER_TO_HOST_2D a rectangle then RESOURCE_FLUSH it, and the host never shows a half-drawn frame. acquire() returns the guest-side framebuffer directly — no separate back buffer is needed, because the host cannot see it until the transfer. wait_vblank is deliberately null: whole rectangles are atomic, so there is nothing to synchronise against, and the wrappers in device.c handle a null op.

Transport details that are easy to get wrong:

  • Structures live at an offset inside whichever BAR the device names in a vendor-specific PCI capability (PCI_CAP_ID_VNDR, 0x09). Assuming BAR 0 works on QEMU and is exactly the assumption that breaks on the next device.
  • The status handshake order is not negotiable: reset → ACK → DRIVER → read features → write features → FEATURES_OK → read FEATURES_OK back (the device clears it to reject, and that readback is the only way it ever tells you) → queues → DRIVER_OK.
  • VIRTIO_F_VERSION_1 is bit 32, so it lives in the second 32-bit window — hence the feature-select registers.
  • Notify address is notify_base + queue_notify_off * notify_off_multiplier; the multiplier is a trailing field of the notify capability only.
  • MMIO accessed at exact widths. The common config has 8/16/32-bit fields adjacent, and a device may ignore a wrong-width access silently.
  • 64-bit queue address registers are written as two 32-bit halves, low first.

Polled, not interrupt-driven: every caller wants the answer before continuing, and the wait is microseconds. The poll loop is bounded so a wedged device cannot hang boot.

/proc/devices name and driver columns are 14 wide because virtio-gpu0 is eleven characters and overran ten.

Not done: cursor queue, multiple scanouts, mode setting (the resolution is whatever the host reports), resource reuse on resolution change, and 3D/virgl.