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
2.5 KiB
| name | description | metadata | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| schunix-framebuffer-device | /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 |
|
/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/gfx/fb.h. Before this it was wired to { nullread, nullwrite } — writing
pixels to it succeeded and did nothing.
include/gfx/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 beforevga_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 throughmmap("/dev/fb0").sys_ioctlanswers 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.