SchOS/.claude/memory/schunix-framebuffer-device.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

55 lines
2.5 KiB
Markdown

---
name: schunix-framebuffer-device
description: /dev/fb0 is a real device with the Linux fbdev ABI; nothing may assume where the framebuffer is. Test with bootcheck.ps1 -Fb and tools/fbshot.ps1
metadata:
node_type: memory
type: project
originSessionId: a665e14b-86bd-4b41-a228-51377f3e9225
modified: 2026-08-04T16:16:17.735Z
---
**`/dev/fb0` (major 2) is a real framebuffer device** as of 2026-08-04:
`kernel/dev/fb.c` (read/write by byte offset, plus `fb_ioctl`), declared in
`include/fb.h`. Before this it was wired to `{ nullread, nullwrite }` — writing
pixels to it succeeded and did nothing.
**`include/fb.h` holds Linux's fbdev request numbers and struct layouts**
(`FBIOGET_VSCREENINFO` 0x4600, `fb_var_screeninfo` 160 bytes,
`fb_fix_screeninfo` 80 bytes). Same reasoning as
[[schunix-linux-open-flags]]: a shared number must mean the shared thing, or a
program gets a differently-shaped answer to a request it thought it
understood.
**Nothing outside `vga.c` may assume where the framebuffer is.** Ask
`vga_fb_phys()`, `vga_fb_size()`, `vga_fb_stride()`, `vga_fb_bpp()`,
`vga_fb_ptr()`. Three places used to hardcode `0xfd000000`, two of them also
hardcoding `800*600*4` — right only on QEMU stdvga at one resolution.
Two traps that cost time here:
- **`setupkvm()` runs before `vga_init()`**, so it cannot know the framebuffer
address. It used to identity-map one anyway, user-writable, into every
process ever created. That mapping is now gone entirely — nothing used it,
since userspace reaches the framebuffer through `mmap("/dev/fb0")`.
- **`sys_ioctl` answers every non-tty request with 0.** Any new device ioctl
must be routed *before* that catch-all, or the caller is told "success" and
reads an untouched buffer — a 0x0 screen that looks like a driver bug rather
than a missing one.
**To check it:**
```
pwsh -File tools/bootcheck.ps1 -Fb # 9 assertions on the reported geometry
pwsh -File tools/fbshot.ps1 -Mode fill # gradient via mmap, screendumped
pwsh -File tools/fbshot.ps1 -Mode bars # colour bars via write()
```
`cmd/fbinfo` is the program those drive. `fill` goes through mmap and `bars`
through the device's `write()` **on purpose**: when one works and the other
does not, the failure says which half broke.
Serial output cannot catch a stride bug — a framebuffer written with the wrong
row pitch reports every byte accepted and merely shears the picture. That is
why fbshot takes an actual screenshot; see
[[schunix-verify-by-driving-shell]]. Convert with `convert` in the WSL box,
not `magick`.