SchOS/.claude/memory/schunix-virtio-gpu.md
Nexoniarz 3182da3965 Carry project memory in the repo, with tools/memory-sync.sh
Claude Code keeps per-project memory at <home>/.claude/projects/<key>/,
where <key> is the project's absolute path with every separator and the
drive colon turned into a dash. That makes it both rename-fragile and
machine-specific, and this project has hit each of those once:

  - D:\DEV\Schunix -> D:\DEV\SchOS changed the key, and 43 memories were
    invisible until moved across by hand.
  - Development happens from Windows and from Linux, and each looks in a
    different place for the same checkout.

Committing .claude/memory/ fixes both -- git carries it across renames
and between hosts -- and `import` puts it wherever the local machine
expects. .claude/settings.local.json stays ignored: those are per-machine
permissions.

The colon maps to a dash rather than being dropped. Deleting it gives
D-DEV-SchOS, which matches nothing and silently finds zero memories; that
was the first attempt. On Git Bash the key comes from `cygpath -w`,
because Claude Code is a Windows program and sees D:\DEV\SchOS, not
/d/DEV/SchOS.
2026-08-09 14:27:22 +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/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.