Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/63-d10-second-body.md
2026-08-10 21:55:43 -04:00

284 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TASK 63 — the second body: SouveraineOS on the iPhone 7
**Status: the shell is on the glass, 2026-08-09.** viewtop, sessiond and
`qs -c souveraine` all run on a T8010 with no GPU, drawn by llvmpipe, over one
USB cable. Wallpaper, palette, bar, clock, dock and her quote match the Pixel.
**Repos:** `souveraine`, `souveraine-viewtop`, `Pixel3Arch`, and whatever owns
device provisioning next.
This task exists because the device is no longer a Hoolock experiment. Hoolock
is the bootloader and nothing else; what is on `nvme0n1p1` is a first-class
Souveraine system, and everything below is about making a second body work
without forking the first.
## What runs, measured
| | |
|---|---|
| board | Apple T8010, `d10ap`, iPhone 7 Global |
| kernel | `7.2.0-rc1-gb902cb53677b-dirty`, DTB `t8010-d10-nowifi` |
| display | `DRIVER=simple-framebuffer`, `/chosen/framebuffer@87de5c000`, one mode `750x1334`, **`card0` only — no `renderD128`** |
| renderer | Mesa 26.1.6, **llvmpipe**, GLES 3.2, `PLATFORM_GBM_KHR` on `card0` |
| scale | 1.0, source `Density`, dpi 96 — simpledrm publishes no EDID, so the physical-mm path resolves through the DPI fallback rather than dividing by zero |
| link | NCM `usb0` at `172.16.43.1/24`; the Pixel keeps `172.16.42.0/24`. No wifi, no modem, no battery node |
## The one that decides the lane: EGL on a card with no render node
`eglinfo`'s GBM platform fails and its surfaceless platform works, which reads
like software GL being unavailable. It is not. **`MESA_LOADER_DRIVER_OVERRIDE=kms_swrast`
is what breaks it** — forcing that loader makes Mesa look for a render device to
pair with the card, find none, and quit:
```
[EGL] 0x3001 NOT_INITIALIZED: DRI2: failed to get compatible render device
```
Drop the override and Mesa finds its own way. Three of four variants come up
listening: bare `swrast`, `GALLIUM_DRIVER=llvmpipe`, and `zink`. Only the
`kms_swrast` override dies.
**`GALLIUM_DRIVER=llvmpipe` is what runs today**, and that is a stopgap: it must
not be set on the Pixel. viewtop already logs `no egl render node; advertising
linux-dmabuf v3` — it *knows* — and should take the software path itself instead
of needing the environment to tell it. That is the fix, and it is small.
## Tearing — the mechanism, so nobody tunes at it
Casey, on the glass 2026-08-09: screen tearing artifacts, often.
viewtop logs at startup:
```
failed to create signaled syncobj — err=Os { code: 95, Unsupported }
```
simpledrm has no syncobj support, so there is no explicit fence to hold a page
flip until the frame is finished. On the Pixel the GPU fences it. Here llvmpipe
writes into a dumb buffer while the panel scans the same memory. This is
structural, not a setting. Fixing it means either a fence-free damage/copy
discipline in the KMS path or double-buffering the dumb BO by hand.
## Touch, and the display pipe — 2026-08-09 night
**Touch works on the glass.** `evtest`-grade proof, not a device node: 2407 events,
270 `SYN_REPORT`, 13 balanced down/up, X 53..735, Y 107..1333, with
`MT_TOUCH_MAJOR` and `MT_ORIENTATION`. Kernel-native `apple_z2` — no `hx-touchd`,
no Sandcastle daemon. Three layers, each hiding the next:
1. **`apple/t8010-smartio.bin` was not on the rootfs.** SIO DMA times out
(`-ETIMEDOUT` after the 60 s sysfs fallback), so `apple-spi` gets no TX DMA
and the SPI bus never probes. Both blobs were built from the IPSW on
2026-08-08 into the *install ramdisk only*, and the disk-boot blob carries no
initramfs. Now `CONFIG_EXTRA_FIRMWARE`, compiled into the Image, which is the
only placement that beats rootfs to the probe.
2. **`# CONFIG_REGULATOR is not set`.** That silently dropped
`REGULATOR_APPLE_PMIC_TOUCH` (`drivers/regulator/apple-pmic-touch-regulator.c`,
marked `default y if ARCH_APPLE`), so `apple_z2`'s mandatory `hv`/`core`
supplies resolved to no-op stubs and the controller was never powered. **A
registered input node with dead rails is indistinguishable from working
touch** — verify with events, never dmesg.
3. **Runtime report activation was gated to D11.**
`apple_z2_enable_d11_reports_locked()` is what switches runtime reports on;
D10 never called it, so boot frames arrived and the panel went silent about a
second later. All four `is_d11()` sites now go through
`apple_z2_is_gen2_reports()` = D10 D11.
**The Apple Display Pipe runs on a T8010 — the first anywhere.**
`[drm] Initialized adp 0.1.0 for 206200000.display-pipe on minor 1`, with
`card1-DSI-1` and the panel attached at `206600000.dsi.0`. Every other port on
this hardware scans out a dumb framebuffer: Sandcastle, pmOS
(`linux-postmarketos-apple-16k` has no `APPLE_SIO` at all), uPhone
(`CONFIG_FB_SIMPLE=y`, `TOUCHSCREEN_HX`, an `hx-touchd.service`), and
qwertyoruiopz's original 5.4.14 boot. That is why the wiki's *"abnormal screen
color"* has no owner.
Addresses came off the D10 ADT and were checked against
`HoolockLinux/linux:tests/adp` commit `2b3b1a7fc` "t8012 dts: display pipe" —
T8012 is T2, A10-family, and its `disp0_dart` is literally `apple,t8010-dart`:
| node | reg | irq |
|---|---|---|
| `disp0` | be `0x06200000/0x9000`, fe `0x06400000/0x4000` | be 154, fe 158 |
| `disp0_dart` | `0x06304000/0x4000` | 156 |
| `disp0_mipi` | `0x06600000/0x100000`**byte-identical to T8012's** | 163 |
The be/fe split is not guesswork: ADP binds only `be`/`fe` by name, pokes `be` to
`0x40f4` and `fe` to `0x100`, and T8012's own IRQ layout has its DART sitting
*between* the pipe's two — 210/212/214 there, 154/156/158 here. `panel-summit` is
134 lines with no init sequence because the loader lights the panel, which is
exactly our situation; it gained a 750×1334 mode under `apple,d10-panel`.
**Getting simpledrm out of the way works. The panel is dark anyway.**
`status = "disabled"` on `framebuffer0` in the DT does nothing — m1n1's `kboot`
fills and re-enables `/chosen/framebuffer` itself, so the loader wins. udev then
names simpledrm the primary GPU and `primary_device()` (`kms.rs:447`) hands
viewtop `card0`. `initcall_blacklist=simpledrm_platform_driver_init` on the
cmdline does remove it, and then ADP *is* `card0`: viewtop opens it, EGL comes
up, the CRTC reports `active=1` with `fb=43` and the plane at `750x1334+0+0`,
the connector is `connected` and `enabled`. Both backlights raised. Still black.
So the compositor is producing frames and the panel is not showing them, and the
two candidates are both in the parts that were *guessed* rather than derived:
- **The mode timings are invented.** Every address in this port came off the ADT
or matched T8012. The raster did not — nothing published one, so
`panel-summit`'s d10 mode carries porches I made up. m1n1 already programmed
Apple's real ones; ADP then re-modesets over them.
- **`panel-summit` never sends a DCS display-on**, and never sets `dsi->lanes`,
`format` or `mode_flags`. That is deliberate for a Touch Bar, where the loader
leaves the panel running. D10's ADT says `#lanes = 2`; if `adp-mipi`
reprograms the host from unset defaults the link comes up wrong.
`boot.sh` therefore keeps simpledrm by default and takes `-a` for the ADP-only
boot. **The pipe is not the daily path until a pixel is seen on it.**
Kernel work is committed on archdev as `2e349ddf2` and exported to
`souveraine-iphone/d10-port/patch/`. That commit also captured the 141-line `t8010-d10.dts`
port, which had been uncommitted against the 14-line mainline stub.
## The session is real now
greetd → `/usr/bin/souveraine-session-viewtop` as `casey`, no greeter, same shape
as blueline. `casey` is uid 1000 with blueline's exact groups
(`wheel input video seat souveraine`); the stock `alarm` account is gone and its
home preserved at `/home/alarm.stock-preserved`. sessiond and the shell run as
supervised user units and are enabled at boot, so §0's `no Display session for
uid 0` drift is gone — `loginctl` shows a real `seat0`/tty1 session and
`LockedHint` tracks it.
Carried across and worth knowing: the QML config is a **symlink farm with
absolute targets**, so moving it between homes needs all 432 links repointed, not
just a copy. Shell tree parity against blueline was verified file-by-file — 1056
files, one difference, and it is a file the *Pixel* lacks.
`souveraine-iphone/boot.sh` is the whole boot: waits for DFU, runs checkm8 → m1n1 → kernel
handoff, then prints kernel/touch/spi/drm/adp/deferred/session. `./boot.sh` for
everything, `1`/`2`/`3` for one stage, `-l` to list staged kernels, `-k` to pick.
## Open on the d10
- **The buttons do nothing.** `gpio-keys` is on event1 and nothing routes it to
sessiond's `button` verb — the blueline path came from `hyprland.lua` bindings
ported into the state machine, and no equivalent exists here. The ADT names
four: `hold`, `volup`, `voldown`, `ringerab` (the mute switch).
- **`Dim` was device-specific and is fixed but unshipped.** `brightnessctl set 10`
is an absolute: 4% of blueline's 255-step backlight, 0.5% of the iPhone's
2047-step Apple DWI, where it reads as fully off. `souveraine eb0292c` makes it
`4%`; it needs a CI build and `pacman -Syu` to reach the device.
- **`touchKeypad` binds before its config loads.** `Lock.qml:137` defaults it
`false` and `:148` binds the surface from that, so the lock came up as the
keyboard variant with no keypad — and `surfaceComponent` cannot change once the
lock is live. Patched on-device only (`Lock.qml.bak-touchkeypad` beside it);
the repo fix is still owed.
- **`casey` has password `123456`.** It is also the lock-screen PIN.
- Sensors report nothing and say so honestly: proximity, light and accel are all
`source-never-reported`, because no reporter exists on this board.
## Where the split is
The device does not need `souveraine-upower-iphone`. It needs the base to stop
assuming one board. pmOS's shape: one common base, thin per-device packages
carrying DT and quirks.
**Belongs in the common base:**
- panel power. sessiond's `Blank`/`Unblank` shell out to
**`blueline-screen-toggle`**, a script name that exists on one phone and ships
only in `rootfs-overlay/`. On the d10 it resolves to nothing, so the panel
*structurally cannot* sleep. viewtop owns the CRTC, has `power.rs` with the
EBUSY deferral, and runs on both — the blank belongs there as a verb.
- render-path selection, per the section above.
- the shell's `hyprctl` calls: `workspaces`, `monitors`, `layers`, `clients`,
`activewindow`, `activeworkspace`, `getoption animations:enabled`. Dead on
both devices under viewtop; the Pixel merely still has the binary lying
around, so the failure is silent there and loud here.
**Belongs in a per-device package:** DTB, the `nowifi` variant, any T8010 quirk,
and the `PHONE_HOST`/subnet identity.
**Neither, and it is a real absence:** no `/sys/class/power_supply` entry and no
modem on this board. The bar is telling the truth about a different animal, not
drifting. Do not paper it.
## What the provisioning has to do, because none of it was possible
That rootfs had **never installed a package in its life**, and could not have:
1. **No Landlock.** `/sys/kernel/security` holds only `lsm`. pacman 7.1 aborts
at *sync*: `restricting filesystem access failed because Landlock is not
supported by the kernel`. The image carried `#DisableSandboxFilesystem` and
`#DisableSandboxSyscalls`**neither is a pacman option**, the same phantom
pair TASK-25 recorded breaking the Pixel in July, propagated into a second
image. pacman 7.1 has exactly one: `DisableSandbox`.
2. **No keyring at all.** `/etc/pacman.d/gnupg` did not exist — not empty,
absent. Even a successful sync would have aborted at verify.
Either one alone is fatal, and both fail in a way that looks like a network
problem. A first-boot provisioning step owes `pacman-key --init`/`--populate`
and a kernel-capability check that writes the real option.
## What had to be hand-carried, and what that says
Every one of these is the overlay-vs-package hole (TASK-27/28) hitting a second
device:
- `/etc/pam.d/souveraine-sessiond` — overlay-only, so sessiond has no PAM stack
on any machine that did not flash. Same reason the laptop has none.
- `blueline-screen-toggle` / `blueline-undim` — overlay-only, so no blank.
- fonts (`ttf-material-symbols-variable` and friends) — AUR-only. Without the
Material Symbols variable font the bar prints ligature names as literal text:
`wifi_find`, `5`. That is what missing icons look like.
- `config.json` with `"phone": true` — without it the shell comes up in desktop
mode and ii throws its first-run wizard over everything.
- the generated palette under `~/.local/state/quickshell/user/generated` — the
colours are derived from the wallpaper, so with no image she renders greyscale.
## Root, and closing the door behind us
The stock ALARM credential (`root`/`root`) was live over SSH with
`PermitRootLogin yes`. Casey's `ani` key is now in `/root/.ssh/authorized_keys`.
**The password path is still open and should be closed** — that is a real
finding about the provisioning, not about this device.
## The lock held, and that is the good news
With qs owning the lock, both refusals fired correctly:
```
viewtop: refusing to open a capture session — "no client captures the screen behind a lock screen"
sessiond: "a live shell owns the session lock; use the shell's lock IPC"
shell: "unlock is the credential gate; not remotely callable"
```
Root on the box, a key in the account, and the session still would not open for
a socket. Doctrine §13's user column enforced by three independent layers on a
device it had never run on before.
Consequence for working here: **a screenshot needs the session unlocked at the
glass.** `grim` behind a lock hangs, and that is the gate doing its job — check
`locked` in `state` before concluding anything from a capture failure
(`DUMP-bugs-2026-08-06` item 7, second instance).
Also measured: on root there is no logind session, so `lockhint` warns
`no Display session for uid 0` and falls back to its own tracking, *which
drifts* — §0's shadow-copy failure, reproduced. The packaged path is greetd plus
a real user session, and this is why.
## Acceptance
- `pacman -S` on a fresh device installs a working Souveraine session with no
hand-carried file, no AUR font, and no environment variable.
- viewtop selects the software render path from what it already detects.
- The panel sleeps and wakes on both devices through one verb.
- The shell asks the compositor for windows and layers, not `hyprctl`.
- A second device's identity is a package, not an edit.
## Connects to
TASK-25/27/28 (the overlay-vs-package hole, three more instances),
TASK-43/51 (viewtop; the render path and the tearing are new lanes),
TASK-08 (`Blank`/`Unblank` are its actuators),
`DEVICE-STATE-MACHINE.md` §0 (lockhint drift, reproduced),
`SESSION-AUTHORITY-DOCTRINE.md` §13 (three layers refused the unlock),
`souveraine-iphone` (the D10 boot path; Hoolock is its upstream provenance,
not the project's identity).