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
55 lines
2.5 KiB
Markdown
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/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 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`.
|