15 KiB
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:
apple/t8010-smartio.binwas not on the rootfs. SIO DMA times out (-ETIMEDOUTafter the 60 s sysfs fallback), soapple-spigets 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. NowCONFIG_EXTRA_FIRMWARE, compiled into the Image, which is the only placement that beats rootfs to the probe.# CONFIG_REGULATOR is not set. That silently droppedREGULATOR_APPLE_PMIC_TOUCH(drivers/regulator/apple-pmic-touch-regulator.c, markeddefault y if ARCH_APPLE), soapple_z2's mandatoryhv/coresupplies 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.- 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 fouris_d11()sites now go throughapple_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-summitnever sends a DCS display-on, and never setsdsi->lanes,formatormode_flags. That is deliberate for a Touch Bar, where the loader leaves the panel running. D10's ADT says#lanes = 2; ifadp-mipireprograms 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-keysis on event1 and nothing routes it to sessiond'sbuttonverb — the blueline path came fromhyprland.luabindings ported into the state machine, and no equivalent exists here. The ADT names four:hold,volup,voldown,ringerab(the mute switch). Dimwas device-specific and is fixed but unshipped.brightnessctl set 10is 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 eb0292cmakes it4%; it needs a CI build andpacman -Syuto reach the device.touchKeypadbinds before its config loads.Lock.qml:137defaults itfalseand:148binds the surface from that, so the lock came up as the keyboard variant with no keypad — andsurfaceComponentcannot change once the lock is live. Patched on-device only (Lock.qml.bak-touchkeypadbeside it); the repo fix is still owed.caseyhas password123456. 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/Unblankshell out toblueline-screen-toggle, a script name that exists on one phone and ships only inrootfs-overlay/. On the d10 it resolves to nothing, so the panel structurally cannot sleep. viewtop owns the CRTC, haspower.rswith the EBUSY deferral, and runs on both — the blank belongs there as a verb. - render-path selection, per the section above.
- the shell's
hyprctlcalls: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:
- No Landlock.
/sys/kernel/securityholds onlylsm. pacman 7.1 aborts at sync:restricting filesystem access failed because Landlock is not supported by the kernel. The image carried#DisableSandboxFilesystemand#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. - No keyring at all.
/etc/pacman.d/gnupgdid 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-variableand 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.jsonwith"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 -Son 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).