ADR 0012
Thunderbolt and USB4 devices are asked about, and the IOMMU is on
Context
A Thunderbolt or USB4 device is connected to the computer as if it were
inside it. Its PCIe tunnel lets it, or anything behind it, read and write
memory by DMA. So most firmware does not let such a device in by itself. It
sets the host controller's security level to user or secure and leaves
the decision to the operating system, which says yes by writing to the
device's authorized file in sysfs.
Nothing in Alpymist did that. On the dev machine (a ThinkPad X1 Carbon on a
ThinkPad Thunderbolt 3 Dock) the dock's screens worked, because DisplayPort
tunnels need no approval, but its keyboard, mouse, network and sound never
appeared. The dock sat at authorized=0 behind a domain at level user.
Every Thunderbolt 3, Thunderbolt 4 and USB4 dock behaves this way on firmware
that asks, from any vendor, on Intel and AMD alike.
Two further facts from that machine:
- The IOMMU was off. The hardware has one (VT-d, listed in the ACPI DMAR
table), but Alpine's
linux-ltsis built withoutCONFIG_INTEL_IOMMU_DEFAULT_ON, and neither the live image nor the installer asked for it. Soiommu_dma_protectionread0: a device let in could reach all of memory. AMD's IOMMU is on by default wherever it exists. - At level
usera device proves nothing. It is known by the UUID it reports, and another device can report the same one. Only at levelsecure, and only with a device that has akeyfile, can a device be given a random key the first time and made to answer a challenge against it afterwards.
The usual tool is bolt (boltd and boltctl), and it is packaged for
Alpine. It does not fit Alpymist. It is started by systemd, through its udev
rule and D-Bus activation, so on OpenRC nothing would run it at boot to let
a known dock back in. Its polkit rule trusts "an active, local wheel user",
and without elogind polkit sees no sessions, so that rule never matches.
Alpymist already has the pattern bolt would replace: a small root helper run
through pkexec, rules keyed on the seat group, and alpymist-auth to ask
for a password (as alpymist-power does).
ADR 0011 decides the rest: nothing is let in that nobody chose to let in.
Decision
1. alpymist-thunderbolt, not bolt
One crate, three parts, nothing vendor-specific:
- The helper,
alpymist-thunderbolt-helper, run as root through pkexec, udev and OpenRC. A closed set of verbs:allow UUID once|always,reconnect UUID,forget UUIDandboot. A device is named by the UUID it reports, checked, and found by reading the bus; no path is taken from the caller. - The store,
/var/lib/alpymist/thunderbolt/.allowed.tomllists the devices allowed always, readable by everyone so the session and Settings can show it.keys/<uuid>holds a key, readable by root alone. - The session's watch,
alpymist-thunderbolt, started with the desktop. It listens for the kernel's uevents, which any user may read, asks about a new device, and lets a remembered one back in.
It reads only what the kernel documents for every Thunderbolt and USB4 host
(Documentation/admin-guide/thunderbolt.rst), and does nothing at the levels
where the firmware decides (none, dponly, usbonly, nopcie).
2. What happens when a device arrives
| The device is… | Unlocked desktop | Locked, or nobody logged in |
|---|---|---|
| already in, or at a level that needs no approval | nothing | nothing |
| unknown | asked about | waits |
| allowed always, known only by its UUID | let in again, without asking | waits |
| allowed always, proves who it is with its key | let in again | let in again |
- Asking is a dialog of ours in the middle of the screen, the rest dimmed.
It names the device, says what letting it in means, and offers Don't
allow, Allow once and Always allow. Don't allow is the highlighted
button, and Enter with nothing focused does nothing. Allowing then asks for
an administrator's password (
org.alpymist.thunderbolt.allow, alwaysauth_admin), because this gives the device the run of the machine. - Always allow says what it means for this device. Where the device can only give a UUID, the dialog says in one sentence that another device could pretend to be this one, and that Always would let that one in too whenever the screen is unlocked. Where it can prove itself, the dialog says that instead.
- Letting a remembered device in again needs no password for the
seatgroup (org.alpymist.thunderbolt.reconnect), as suspending does not. The administrator already decided. The helper refuses any device that is not in the store. - Never while locked, never at the login screen, unless the device proves
who it is. A lock that let in any device reporting the right UUID would
guard nothing. The cost: a laptop that boots docked with its lid shut, at
level
user, cannot use the dock's keyboard to log in. Open the lid, or use a device that can take a key. - At level
securethe first Allow writes a fresh 32-byte key into a device that has akeyfile. Always keeps that key, and from then on the device must answer a challenge (authorized=2). udev and the OpenRC service do this at boot and on hotplug, before anyone logs in.
3. The IOMMU is on by default
The installer passes KERNELOPTS="quiet intel_iommu=on" to setup-disk, and
the live image's kernel command line carries intel_iommu=on, on every tier.
With the IOMMU on, a device can reach only what its driver mapped for it, and
on firmware that marks its Thunderbolt ports (iommu_dma_protection=1) the
kernel also bounces untrusted devices' DMA.
It is a small risk on old or buggy chipsets, the Potato tier's in particular,
where VT-d firmware tables are sometimes wrong. The remedy is the usual one:
intel_iommu=off on the command line, from GRUB's editor once and in
/etc/default/grub for good. That is a choice to make the machine less safe,
and per ADR 0011 it is the owner's to make, not ours to make for everyone.
Installed systems are not changed on upgrade. Rewriting a machine's boot configuration from a package script would be a surprise even when it is the safe direction.
Consequences
- A Thunderbolt or USB4 dock works on Alpymist after one question and one password, and comes back without either while the screen is unlocked.
- On a
user-level laptop, a remembered dock waits for an unlocked session, so docked boots with the lid shut need the lid opened to log in. - The Legacy tier (i3, X11) has no layer shell for the dialog and does not
install the package. There,
alpymist-thunderbolt-helper allow UUID oncethroughdoasis the way until it has an answer of its own. - The live image and the installer do not ask yet: someone installing from a
docked laptop at level
userhas to use the built-in keyboard. Asking on the console there is tracked in #18. iommu_dma_protectiondepends on the firmware as well as the IOMMU. The dev machine's 2018 firmware may never report1. The IOMMU still confines devices, but that bit is not ours to set.
Addendum, 2026-09-27: tried on the dev machine
§3 has now run on hardware: the ThinkPad X1 Carbon 6th gen above, on firmware
N23ET93W (1.68), booted with intel_iommu=on and then used for a desktop
session with the ThinkPad Thunderbolt 3 Dock plugged in.
- Nothing broke. It booted to the login screen and the desktop as before.
i915loaded in an IOMMU group of its own, both remapping units (dmar0,dmar1) came up, and the kernel logged no DMA faults and no GPU errors. - The dock is confined. The watch let it in as allowed always, and
everything behind it appeared. The dock's USB controller, which reaches the
machine as PCIe through the tunnel, sat in its own group with translated
DMA (
DMA-FQ), so it could reach only what its driver mapped. iommu_dma_protectionstays0, as the last consequence above expected. The firmware's ACPI DMAR table has flags1: interrupt remapping, without the platform opt-in bit (4) that tells the kernel the firmware protected the Thunderbolt ports before handing over. Without it, the kernel does not mark those ports untrusted. Their devices get lazy IOTLB invalidation rather than strict, and no bounce buffers, and the time before the kernel turns the IOMMU on is not covered. The IOMMU still does most of the work. Before, the dock could reach all of memory; now it can reach only what its driver mapped.
This is one machine, and the risk to older chipsets in §3 is unchanged.
Whoever wants to try the IOMMU on a machine without betting its next boot on
it can do what was done here. Put a copy of the GRUB entry, with
intel_iommu=on added, in /boot/grub/custom.cfg with --id iommu-test,
and run grub-reboot iommu-test. That boots it once, and holding the power
button gets the old boot back.
A login loop on this machine was blamed on intel_iommu=on before this test,
and it was not the cause. A hand-written init script held up the boot before
seatd started, so every login went to a Hyprland that could not open a seat.
A desktop that dies at once after a change to the kernel command line is
worth checking for that before blaming the IOMMU.