input, fb, vt, sysfs: the kernel calls Xorg makes

Xorg on a framebuffer -- the fbdev video driver, the evdev input driver,
libpciaccess -- asks the kernel a fixed set of questions before it draws
anything, and gives up on the first wrong answer. These are those
answers.

evdev: EVIOCGVERSION, GID, GNAME, GPHYS, GUNIQ, GPROP, GBIT, GKEY, GABS,
GREP and the accepted setters. Xorg and libinput decide what a device
*is* from the capability bitmaps; /dev/input/eventN answered only
EVIOCGRAB, which reads as no device at all. Each device's bitmaps are
its defaults plus whatever it has reported, so a USB tablet makes the
pointer absolute.

fbdev: FBIOPUT_VSCREENINFO succeeds for the mode already set, as on
Linux -- the fbdev driver sets it every time it starts -- and still
refuses any other. FBIOPAN_DISPLAY to the origin; FBIOBLANK.

The console: KDSETMODE(KD_GRAPHICS) stops it drawing on a terminal,
KDSKBMODE(K_OFF) stops keys reaching its tty, and VT_PROCESS now does
the switch handshake: leaving the terminal sends relsig and waits for
VT_RELDISP, arriving sends acqsig. A terminal whose owner has died goes
back to the kernel whole, so a crashed X cannot leave the console dead.

/sys: boot_vga, a writable enable, and graphics/fb0 under the display
device, which is how the fbdev driver pairs a card with /dev/fb0.

ports/linux-headers: <linux/*.h> into musl's prefix, from sabotage-
linux's kernel-headers 4.19.88-2. Its checksum is recorded from the
first download; upstream publishes none.

cmd/xready makes each call and checks the answer, including the switch
handshake on tty2 and an owner that dies holding it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-24 12:06:04 +02:00
commit b1323e193a
14 changed files with 860 additions and 32 deletions

View file

@ -4,7 +4,8 @@
//
// /sys/bus/pci/devices/0000:00:1f.2/ vendor device class revision irq
// subsystem_vendor subsystem_device
// resource config uevent
// resource config uevent boot_vga
// enable graphics/fb0/
// /sys/bus/usb/devices/1-3.2/ idVendor idProduct bcdDevice busnum
// devnum speed bDevice* uevent
// /sys/firmware/acpi/tables/DSDT the tables, byte for byte
@ -40,6 +41,7 @@
#include "dev/pci.h"
#include "dev/usb.h"
#include "core/hwinfo.h"
#include "gfx/gfx.h"
// ---------------------------------------------------------------------------
// Inode numbers: kind, index, file -- the whole identity, since iget gives
@ -87,11 +89,43 @@ static const struct { const char *name; int parent; } dirs[D_NDIRS] = {
static const char *const pcifiles[] = {
0, "vendor", "device", "class", "revision", "subsystem_vendor",
"subsystem_device", "irq", "resource", "config", "uevent",
"subsystem_device", "irq", "resource", "config", "uevent", "boot_vga",
"enable",
};
#define PCI_CONFIG 9
#define PCI_ENABLE 12
#define NPCIFILES ((int)(sizeof pcifiles / sizeof pcifiles[0]))
// Two directories under the display device only: graphics/ and graphics/fb0/.
// Xorg's fbdev driver finds the framebuffer that belongs to a PCI device by
// looking for exactly that path, and libpciaccess picks the primary card by
// boot_vga. Empty, because the path existing is the whole answer.
#define PCI_GRAPHICS 13
#define PCI_FB0 14
// The display device: the one whose memory the framebuffer is in, which is
// what boot_vga means on Linux -- the card firmware brought up and drew on.
// Failing that, the first display controller. -1 when there is neither.
static int
boot_vga_idx(void)
{
uint64 fb = gfx_fb_phys();
int first = -1;
for (int i = 0; i < pci_count(); i++) {
struct pci_dev *d = pci_get(i);
if (d->class != 0x03)
continue;
if (first < 0)
first = i;
for (int b = 0; fb && b < 6; b++)
if (!d->bar[b].is_io && d->bar[b].base && fb >= d->bar[b].base &&
fb < d->bar[b].base + d->bar[b].size)
return i;
}
return first;
}
static const char *const usbfiles[] = {
0, "idVendor", "idProduct", "bcdDevice", "busnum", "devnum", "speed",
"bDeviceClass", "bDeviceSubClass", "bDeviceProtocol", "bNumConfigurations",
@ -276,6 +310,12 @@ children(uint64 dino, child_fn cb, void *ctx)
for (int f = 1; f < NPCIFILES; f++)
if (cb(ctx, INO(K_PCI, idx, f), 0, pcifiles[f]))
return 1;
if (idx == boot_vga_idx() && gfx_fb_phys() &&
cb(ctx, INO(K_PCI, idx, PCI_GRAPHICS), 1, "graphics"))
return 1;
} else if (kind == K_PCI && I_FILE(dino) == PCI_GRAPHICS) {
if (cb(ctx, INO(K_PCI, idx, PCI_FB0), 1, "fb0"))
return 1;
} else if (kind == K_USB && I_FILE(dino) == 0) {
for (int f = 1; f < NUSBFILES; f++)
if (cb(ctx, INO(K_USB, idx, f), 0, usbfiles[f]))
@ -293,7 +333,10 @@ parent_of(uint64 dino)
{
switch (I_KIND(dino)) {
case K_DIR: return INO(K_DIR, dirs[I_IDX(dino)].parent, 0);
case K_PCI: return INO(K_DIR, D_PCIDEV, 0);
case K_PCI:
if (I_FILE(dino) == PCI_GRAPHICS) return INO(K_PCI, I_IDX(dino), 0);
if (I_FILE(dino) == PCI_FB0) return INO(K_PCI, I_IDX(dino), PCI_GRAPHICS);
return INO(K_DIR, D_PCIDEV, 0);
case K_USB: return INO(K_DIR, D_USBDEV, 0);
case K_MEMMAP: return INO(K_DIR, D_MEMMAP, 0);
}
@ -363,6 +406,13 @@ gen_pci(struct out *o, struct pci_dev *d, int f)
case 5: puts_(o, "0x"); putx(o, svid, 4, 0); break;
case 6: puts_(o, "0x"); putx(o, sdid, 4, 0); break;
case 7: putu(o, d->irq == 0xFF ? 0 : d->irq); break;
case 11: putc_(o, d == pci_get(boot_vga_idx()) ? '1' : '0'); break;
case PCI_ENABLE: {
// Whether memory or I/O decoding is on: what "enabled" amounts to.
uint32 cmd = pci_read32(d->bus, d->slot, d->func, 0x04);
putc_(o, (cmd & 3) ? '1' : '0');
break;
}
case 8:
// start, end, flags per BAR, then the ROM, as Linux prints them.
for (int b = 0; b < 7; b++) {
@ -487,6 +537,8 @@ exists_dir(uint64 ino)
case K_DIR:
return I_IDX(ino) < D_NDIRS && (I_IDX(ino) != D_EFI || boot_is_efi());
case K_PCI:
if (I_FILE(ino) == PCI_GRAPHICS || I_FILE(ino) == PCI_FB0)
return I_IDX(ino) == boot_vga_idx() && gfx_fb_phys() != 0;
return I_IDX(ino) < pci_count();
case K_USB: {
struct usb_info u;
@ -514,7 +566,9 @@ sysfs_iload(struct inode *ip)
ip->size = 0;
ip->nlink = 1;
if (I_FILE(ino) == 0 && exists_dir(ino)) {
int subdir = I_KIND(ino) == K_PCI &&
(I_FILE(ino) == PCI_GRAPHICS || I_FILE(ino) == PCI_FB0);
if ((I_FILE(ino) == 0 || subdir) && exists_dir(ino)) {
ip->type = T_DIR;
ip->nlink = 2;
return;
@ -533,6 +587,8 @@ sysfs_iload(struct inode *ip)
kmfree(buf);
if (n >= 0) {
ip->type = T_FILE;
if (I_KIND(ino) == K_PCI && I_FILE(ino) == PCI_ENABLE)
ip->rwmode = 3;
return;
}
}
@ -585,9 +641,18 @@ sysfs_read(struct inode *ip, int user_dst, uint64 dst, uint64 off, uint n)
return r;
}
// Only `enable`, which libpciaccess writes before it touches a device. The
// kernel's drivers already turned decoding on for every device they use, and
// one nobody drives does not need it -- so the write is accepted and changes
// nothing, which is what it would have done on Linux for a device already on.
static int
sysfs_write(struct inode *ip, int u, uint64 s, uint64 o, uint n)
{ (void)ip; (void)u; (void)s; (void)o; (void)n; return -EROFS; }
{
(void)u; (void)s; (void)o;
if (I_KIND(ip->inum) == K_PCI && I_FILE(ip->inum) == PCI_ENABLE)
return (int)n;
return -EROFS;
}
struct find { const char *name; uint64 ino; };