--- 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`.