diff --git a/README.md b/README.md index 7150da7..e8244db 100644 --- a/README.md +++ b/README.md @@ -204,8 +204,8 @@ Another: **syscalls are declared in exactly one place**, - **No GPU acceleration.** Xorg draws in software; the plan is Linux's `amdgpu` through LDLS ([docs/plan/gpu.md](docs/plan/gpu.md)). There is no IOMMU either, so a driver in a process is isolated against its bugs, not against malice. -- **No udev.** Xorg is told its input devices by a config file instead of - finding them; `/sys` has no device tree and no uevent socket. +- **udev sees the kernel's own devices only.** eudev runs and Xorg finds its input devices + through it; PCI and USB devices are not yet in the `/sys/devices` tree. - **WebKit browsers do not start.** NetSurf does; Epiphany's web process dies at start-up, and it has not been run to ground. - **USB 2.0 controllers (EHCI)** have no driver. On older machines the ports diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 025fa1f..2afb526 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -94,6 +94,7 @@ Read in this order if you are new: | [fat.md](fat.md) | FAT12/16/32 with long names: USB sticks, SD cards, the ESP. | | [devices.md](devices.md) | Buses, classes, binding, and the vtables consumers talk to. | | [usb.md](usb.md) | xHCI, the USB core, and the three class drivers. | +| [udev.md](udev.md) | eudev on SchOS: the device table, the `/sys` tree, uevents, datagram sockets. | | [graphics.md](graphics.md) | Framebuffer, `/dev/fb0`, text rendering, the console, PSOGM and GMMOS. | | [desktop.md](desktop.md) | Xorg on the framebuffer, Window Maker, and what they stand on. | | [packages.md](packages.md) | The apk root: Alpine's packages, and SchOS as one of them. | diff --git a/docs/architecture/desktop.md b/docs/architecture/desktop.md index 92b80c8..c5b45e7 100644 --- a/docs/architecture/desktop.md +++ b/docs/architecture/desktop.md @@ -49,11 +49,9 @@ the real program is for. `xterm` runs and exits, and the server stays up. - **`/root/.xinitrc`** is `exec wmaker`. Without it xinit falls back to twm and an xterm, which are not in the image. -- **`/etc/X11/xorg.conf.d/10-schos.conf`** names the input devices. Alpine's Xorg asks udev for - them, SchOS has no udev, and X came up with no keyboard and no mouse: the pointer did not - move and nothing could end the session. SchOS has exactly two input nodes whatever the - machine, so a fixed list is the whole answer (`AutoAddDevices` off, Ctrl+Alt+Backspace - enabled). +- **`/etc/X11/xorg.conf.d/10-schos.conf`** holds what the defaults do not give: Ctrl+Alt+Backspace + enabled, the keyboard options, and the framebuffer driver. Input devices are *found*, not + named: Xorg asks libudev, which reads what `udevd` wrote to `/run/udev` ([udev.md](udev.md)). - **`/etc/asound.conf`** is for sound, not X ([audio.md](audio.md)). ## Using it @@ -71,8 +69,9 @@ through ACPI, and reach X as a mouse: pointer, click and tap. - **No acceleration.** Software rendering to the framebuffer: no DRM, no glamor, no DRI, no OpenGL beyond what software gives. GPU drivers are a plan ([../plan/gpu.md](../plan/gpu.md)). -- **No udev.** Input devices are named in a config file, not found; `/sys` has no `devices` - tree and no uevent socket. +- **udev covers the kernel's own devices only.** Input, terminals, memory devices, the + framebuffer and disks are announced; PCI and USB devices are in `/sys/bus/*/devices` but are + not yet in the `/sys/devices` tree or on the uevent socket ([udev.md](udev.md)). - **Touchpads are mice**: no two-finger scroll, no gestures, no palm rejection. - **No resolution change from inside X.** The mode is the console's; `schmode` changes the `vgpud` head only, and the loader's menu is where a real panel's mode is chosen. diff --git a/docs/architecture/filesystem.md b/docs/architecture/filesystem.md index bde8768..c60488e 100644 --- a/docs/architecture/filesystem.md +++ b/docs/architecture/filesystem.md @@ -275,10 +275,15 @@ at the moment it is read, so a stick shows up in `/sys/bus/usb/devices` as soon as it is attached. Text files report size 0, as `/proc`'s do; binary ones (the tables, `config`) report their real length, since tools copy them by it. -What is missing, compared with Linux: `/sys/devices` and its symlinks (the +The kernel's own devices (input, tty, mem, graphics, block) have the Linux tree: +`/sys/devices/virtual//` with `dev`, `uevent` and a `subsystem` +link, `/sys/class//` and `/sys/dev/{char,block}/MAJ:MIN` as symlinks +into it, and input capabilities; `uevent` is writable, and a write is announced +on the uevent netlink socket ([udev.md](udev.md)). + +What is missing, compared with Linux: PCI and USB devices in that tree (the `bus/*/devices` entries are directories rather than links into it), -`/sys/class` beyond `dmi` and `power_supply`, `/sys/block`, a uevent socket (so -no udev), and anything writable. PCI `config` is +`/sys/class` entries for them, `/sys/block`, and most of what is writable. PCI `config` is 256 bytes, because config space is read through port `0xCF8`, which reaches no further. diff --git a/docs/architecture/networking.md b/docs/architecture/networking.md index 029e53a..a7b9fa5 100644 --- a/docs/architecture/networking.md +++ b/docs/architecture/networking.md @@ -649,7 +649,11 @@ information was there and the code did not ask. `SO_BROADCAST`, `TCP_NODELAY` reach lwIP's pcb; `SO_SNDBUF`/`SO_RCVBUF` report the real size; `SO_PASSCRED` on Unix sockets) and refuses the rest with `ENOPROTOOPT`. Notably absent: `SO_RCVTIMEO`/`SO_SNDTIMEO`. -- **`SOCK_DGRAM` over AF_UNIX does not exist** — stream only. +- **`SOCK_DGRAM` and `SOCK_SEQPACKET` over AF_UNIX** keep message boundaries (each message carries a + 16-byte header in the same ring) but have no unconnected form: `socketpair` and listeners work, + `sendto` a path does not. +- **Netlink `NETLINK_KOBJECT_UEVENT`** is the one multicast family: sockets bound to group 1 hear the + kernel's device events, group 2 what udevd re-announces, and a datagram can be addressed to a port id. - **No IPv6**, no firewall or packet filter of any kind, and a TCP stack that has been used for downloads at tens of megabits and no more than that. - **Three drivers in the kernel, one Linux driver outside it**, and the class is diff --git a/docs/architecture/udev.md b/docs/architecture/udev.md new file mode 100644 index 0000000..ddba380 --- /dev/null +++ b/docs/architecture/udev.md @@ -0,0 +1,82 @@ +# udev + +Alpine's `eudev` runs on SchOS as an ordinary process, started by `schinit` +(`etc/schinit/udevd.job`, with `udev-trigger.job` replaying the devices that +were there first). It is what lets Xorg *find* its keyboard and mouse instead of +being told, and the road to a Wayland compositor starts here: libinput and +libseat both ask udev what exists. + +The kernel's half is three pieces, all small, none of them specific to udev. + +## The device table: `kernel/dev/kdev.c` + +The devices the kernel implements itself — input event nodes, the memory +devices, terminals, the framebuffer, the disks — are listed once, with their +class, name, `/dev` name and parent. Everything below is generated from it. + +The device numbers are the ones `stat()` reports for the node, not SchOS's +internal ones: `/dev/tty1` is `4:1` to a Linux program (`linux_tty_rdev` in +`kernel/sys/sys_fs.c`), and udev compares what it reads in `/sys` with what +`stat()` says about the node. `dev_stat_numbers()` is the one mapping both use. + +## `/sys`: `kernel/fs/sysfs.c` + +``` +/sys/devices/virtual/input/input0/ name capabilities/{ev,key,rel,abs} properties +/sys/devices/virtual/input/input0/event0/ dev uevent subsystem -> ../../../../../class/input +/sys/class/input/event0 -> ../../devices/virtual/input/input0/event0 +/sys/dev/char/13:64 ... -> ../../devices/virtual/... +``` + +Symlinks are real (`T_SYMLINK` inodes whose target is generated). `uevent` is +readable (`DEVPATH=`, `SUBSYSTEM=`, `MAJOR=`, `MINOR=`, `DEVNAME=`) and +writable: `echo add > uevent` — which is what `udevadm trigger` does — makes the +kernel announce the device again. A write to a PCI or USB device's `uevent` is +accepted and says nothing yet, because those devices are not in the +`/sys/devices` tree and an event's `DEVPATH` has to name something that exists. + +The input capability files are what `input_id` reads to decide +`ID_INPUT_KEYBOARD` or `ID_INPUT_MOUSE`. SchOS has two input devices whatever +the machine — every keyboard on `event0`, every mouse on `event1` +(`kernel/dev/input.c`) — so each has a fixed, plausible capability set. + +## `/dev`: `kernel/fs/devfs.c` + +devtmpfs is no longer read-only: udev creates `/dev/char/MAJ:MIN`, `/dev/fd`, +`/dev/input/by-*` and the like. A small in-memory overlay (192 entries) holds +directories and symlinks, listed after the kernel's own nodes in whichever +directory they are in. Device nodes themselves are still the kernel's: `mknod` +and regular files in `/dev` are refused. + +## The sockets: `kernel/net/unix.c` + +- **`AF_UNIX` `SOCK_DGRAM` and `SOCK_SEQPACKET`** keep message boundaries. + udevd's control socket is `SEQPACKET`, and its manager and workers talk over + a `DGRAM` socketpair. A message carries its sender's credentials with it + (several workers share one end), a zero-length datagram is a datagram, and a + datagram socket never reads end-of-stream. +- **`AF_NETLINK` / `NETLINK_KOBJECT_UEVENT`** sockets bind to multicast groups: + group 1 is the kernel's events, group 2 is what udevd re-announces once its + rules have run. A datagram can also be addressed to a port id — udevd hands + an event to an idle worker that way, and the worker believes it only from the + manager's port. The kernel's messages arrive as credentials with uid 0, pid 0. + A queue that is full drops the message, as Linux does, and `udevadm trigger` + is how a reader recovers. + +## What is not there + +- PCI and USB devices in `/sys/devices`, and `add`/`remove` events from the + hotplug thread. A USB keyboard works because every keyboard is `event0`, not + because udev knew it arrived. +- `hwdb` (`/etc/udev/hwdb.bin`): `udevadm hwdb --update` has not been run in the + image, so `60-input-id` logs "returned non-zero" for the hwdb import. The + properties that matter come from `input_id`. +- Block device partitions, `by-id` / `by-uuid` links, and a `/sys/block`. +- `libinput` and `libseat` (next; see [../plan/roadmap.md](../plan/roadmap.md)). + +## Testing + +Boot the ISO and run `udevd --debug &`, then `udevadm trigger; udevadm settle`. +`/run/udev/data/c7:0` should contain +`ID_INPUT_KEYBOARD=1`, and `udevadm info /dev/input/event0` should print the +device. Under X, `/var/log/Xorg.0.log` says `config/udev: Adding input device`. diff --git a/docs/plan/1.0.md b/docs/plan/1.0.md index ee7ab5c..9a44833 100644 --- a/docs/plan/1.0.md +++ b/docs/plan/1.0.md @@ -114,7 +114,7 @@ it lives. | `mprotect` is a no-op, nothing has NX | [kernel.md](../architecture/kernel.md) | | No routing table | first interface wins, which is a rule and not routing — [networking.md](../architecture/networking.md) | | No descriptor garbage collector | a socket passed over itself and never received leaks the pair | -| No udev / uevent socket | Xorg is told its input devices — [desktop.md](../architecture/desktop.md) | +| udev sees only the kernel's own devices | PCI and USB are not in the `/sys/devices` tree — [udev.md](../architecture/udev.md) | | No EHCI driver | USB 2.0 ports wired only to EHCI do nothing — [usb.md](../architecture/usb.md) | | Touchpads are mice | no two-finger scroll — [desktop.md](../architecture/desktop.md) | | `ru_stime` is zero | [kernel.md](../architecture/kernel.md) | diff --git a/docs/plan/roadmap.md b/docs/plan/roadmap.md index deb7a14..0ee2c1c 100644 --- a/docs/plan/roadmap.md +++ b/docs/plan/roadmap.md @@ -62,11 +62,10 @@ compositor worth running is years of work that Alpine already packages.) The des Wayland compositor (labwc, sway, weston, a GNOME or KDE session) is the larger step. What Wayland — and a modern X — need from the system, in the order they are useful: -1. **udev.** Today Xorg is *told* its input devices (`xorg.conf.d/10-schos.conf`) because nothing announces them. The fix is - Alpine's `eudev` (or `udev`) running on SchOS, which wants: a **uevent netlink socket** (`NETLINK_KOBJECT_UEVENT`, with the - `add`/`remove`/`change` messages and their environment), a real **`/sys/devices` tree** with `/sys/bus/*/devices/*` and - `/sys/class/*/*` as symlinks into it, `uevent` files that can be written to replay events (`udevadm trigger`), and devtmpfs - that udev can create nodes in. libinput then enumerates evdev devices through it. +1. **udev — done for the kernel's own devices** ([udev.md](../architecture/udev.md)). eudev runs from `schinit`, the uevent + netlink socket and the `/sys/devices` tree exist, devtmpfs takes udev's links, and Xorg finds its keyboard and mouse + through it. **Left:** PCI and USB devices in the same tree, with `add`/`remove` events from the hotplug thread, so a USB + keyboard or a GPU appears to udev; libinput then enumerates evdev devices through it. 2. **libinput and libseat** (seatd is in Alpine): evdev with the properties libinput reads (`ID_INPUT_*` from udev), and a way for a non-root compositor to take the keyboard and the display from the kernel — the `VT_PROCESS` handshake exists. 3. **A DRM device.** Wayland compositors want `/dev/dri/card0` with KMS, and Mesa wants `renderD128`. SchOS has neither: it has diff --git a/etc/schinit/udev-trigger.job b/etc/schinit/udev-trigger.job new file mode 100644 index 0000000..aaf57d6 --- /dev/null +++ b/etc/schinit/udev-trigger.job @@ -0,0 +1,14 @@ +# Replay the devices that were already there when udevd started, so /run/udev +# knows about them. Runs once; see /etc/udev-trigger. +# +# udevd is started by its own job and schinit has no ordering, so the script +# waits for the daemon's control socket. + +Label = os.schos.udev-trigger +Program = /bin/sh +Args = sh /etc/udev-trigger +RunAtLoad = yes +KeepAlive = no + +StandardOut = /dev/tty1 +StandardError = /dev/tty1 diff --git a/etc/schinit/udevd.job b/etc/schinit/udevd.job new file mode 100644 index 0000000..4e8a55e --- /dev/null +++ b/etc/schinit/udevd.job @@ -0,0 +1,16 @@ +# eudev's daemon: the thing that turns the kernel's uevents into /run/udev +# data and /dev symlinks, which is how Xorg and every Wayland compositor learn +# what input and display devices exist. +# +# Alpine's eudev package; the kernel side is the uevent netlink socket and the +# /sys device tree (kernel/dev/kdev.c, kernel/fs/sysfs.c). + +Label = os.schos.udevd +Program = /sbin/udevd +Args = udevd +RunAtLoad = yes +KeepAlive = yes +ThrottleInterval = 3 + +StandardOut = /dev/tty1 +StandardError = /dev/tty1 diff --git a/etc/udev-trigger b/etc/udev-trigger new file mode 100644 index 0000000..52f13ab --- /dev/null +++ b/etc/udev-trigger @@ -0,0 +1,9 @@ +#!/bin/sh +# Wait for udevd to listen, then ask the kernel to announce every device again. +i=0 +while [ ! -S /run/udev/control ] && [ $i -lt 100 ]; do + sleep 0.1 + i=$((i + 1)) +done +udevadm trigger --action=add +udevadm settle --timeout=60 diff --git a/kernel/net/unix.c b/kernel/net/unix.c index 84adce7..13d992f 100644 --- a/kernel/net/unix.c +++ b/kernel/net/unix.c @@ -15,15 +15,18 @@ // is the property sleep/wakeup actually requires. Thirty-two sockets carrying // GUI messages will not notice the contention. // -// **Names are held in a table here, not in the filesystem.** A real Unix -// socket appears in a directory listing as a special file, and binding one -// creates that node. Doing that properly means a new inode type threaded -// through both filesystems, iget, dirlookup and stat -- a VFS change, not a -// socket change. The table gives every behaviour that matters: a client -// finds a server by a name it agreed in advance, which is the whole job. -// What is lost is `ls -l /tmp/schcom` showing an `s`, and `unlink()` of a -// socket path failing with ENOENT. Both are worth fixing when the VFS next -// gets opened up; neither blocks anything. +// **Names live in the filesystem.** bind() creates a socket node +// (kernel/sys/sys_net.c) and connect() resolves a path to the inode and looks +// for the socket bound to that inode, so every spelling of a path arrives at +// the same place and the node can be stat()ed and unlink()ed. +// +// **Datagram and sequenced-packet sockets** share the ring with streams: each +// message is a 16-byte header -- length and multicast groups, sender port or +// pid, sender uid and gid -- and then its bytes, so a read takes exactly one +// message and credentials belong to the message, not to the socket. udev's +// workers share one end of a socketpair and the manager has to know which of +// them wrote what. The kernel's uevents travel the same way, to netlink +// sockets of protocol NETLINK_KOBJECT_UEVENT. #include "core/types.h" #include "core/defs.h" diff --git a/tools/posix/mkaroot.sh b/tools/posix/mkaroot.sh index dcfb269..572fe10 100644 --- a/tools/posix/mkaroot.sh +++ b/tools/posix/mkaroot.sh @@ -121,37 +121,26 @@ cat > "$P/root/.xinitrc" <<'XEOF' exec wmaker XEOF -# X's input devices, named rather than found. Alpine's Xorg asks udev for -# them, SchOS has no udev, and X came up with no keyboard and no mouse: the -# pointer did not move, Ctrl+Alt+F-keys did nothing and nothing could end the -# session. SchOS has exactly two input nodes whatever the machine -- every -# keyboard on event0, every mouse on event1 (kernel/dev/input.c) -- so a -# fixed list is the whole answer. The file is SchOS's, in xorg.conf.d, which -# no Alpine package writes into. +# X's input devices are found, not named: udevd (etc/schinit/udevd.job) turns +# the kernel's uevents into /run/udev data, and Xorg asks libudev for every +# device with ID_INPUT set. That was a hand-written list of event0 and event1 +# while SchOS had no udev. The file is SchOS's, in xorg.conf.d, which no Alpine +# package writes into; what is left here is the options the defaults do not +# give, and the framebuffer driver. mkdir -p "$P/etc/X11/xorg.conf.d" cat > "$P/etc/X11/xorg.conf.d/10-schos.conf" <<'XEOF' -# SchOS has no udev, so X is told its input devices instead of finding them. -# event0 is every keyboard and event1 every mouse (kernel/dev/input.c). Section "ServerFlags" - Option "AutoAddDevices" "false" # Ctrl+Alt+Backspace ends X. Option "DontZap" "false" EndSection -Section "InputDevice" - Identifier "keyboard" - Driver "evdev" - Option "Device" "/dev/input/event0" +Section "InputClass" + Identifier "SchOS keyboards" + MatchIsKeyboard "on" Option "XkbRules" "evdev" Option "XkbOptions" "terminate:ctrl_alt_bksp" EndSection -Section "InputDevice" - Identifier "mouse" - Driver "evdev" - Option "Device" "/dev/input/event1" -EndSection - Section "Device" Identifier "fb" Driver "fbdev" @@ -165,8 +154,6 @@ EndSection Section "ServerLayout" Identifier "schos" Screen "screen" - InputDevice "keyboard" "CoreKeyboard" - InputDevice "mouse" "CorePointer" EndSection XEOF