usb: the xHCI controller arrives by delegation, not by claiming

The first driver to stop claiming its own hardware. The device manager holds
the controller and transfers it in the hello reply, so its matching becomes
authoritative instead of advisory — until now the driver claimed the id it
found in argv[1], and any process could have claimed the same integer first.

The manager claims before it spawns, so there is no window in which anything
else could take the device, and transfers in onHello using invocation.sender
— the kernel-stamped task id, which cannot be forged by the caller. hello is
synchronous, so the transfer has completed before the reply lands: no gap
between being told yes and holding the thing.

usb-xhci-bus's hello moves from after controller bring-up to before anything
that needs the device, which is the bring-up reorder the design predicted.
It is the first member of an explicit delegated set, so every unconverted
driver keeps claiming exactly as before and the suite stays green; the set
and device_claim both go at D6. D3 and D4 could not be separated and the
plan records why: the moment the manager claims, any driver still calling
device_claim is refused, and D3 applied to nothing changes no behaviour and
cannot be tested.

This step introduced a regression and the incremental conversion is what
caught it. confineDevice runs inside systemDeviceClaim, so a device arriving
by transfer was never confined for its new owner. Three IOMMU+USB cases
failed on the driver's DMA rings going unbound, and two worse consequences
were latent: a manager death would have torn down a domain a live driver was
using, and a driver death would have leaked one. iommu.reassign now moves
the confinement with the device, keeping the domain and its attachment
intact so it never translates through nothing. Converting all five drivers
at once would have produced the same three failures with five suspects.

A log line of mine claimed "holding controller device N" before anything
verified it — it printed even in the failure case, where the driver held
nothing. Reworded to state only what is known there: where the registers
are.

usb-hid asserts the delegation with the device id backreferenced, so the id
delegated and the id the driver ends up with must match. Emptying the
delegated set fails it with "hello acknowledged" then "mmio_map failed".

usb-hub failed once in a full run and has passed six times since (four
isolated, two full) — recorded in the plan as a suspected instance of the
known intermittent AP fault, not dismissed, since this step did shift boot
timing.

Suite 118/118.
This commit is contained in:
Daniel Samson
2026-08-08 17:55:59 +01:00
parent 33376f24ab
commit a2d6056772
6 changed files with 134 additions and 13 deletions
+28 -3
View File
@@ -41,8 +41,8 @@ that cannot safely run in user space.**
|---|---|---|
| D1 | `device_transfer(device_id, task_id)` — the holder gives a device away | **done** — syscall 54; a move, not a copy |
| D2 | Adversarial case: a process handed nothing is refused, on a held device and a free one | **done** — `device-authority-test`; the claim half joins it at D6 |
| D3 | The manager claims the seeded devices at boot, before any driver is spawned | not started |
| D4 | `usb-xhci-bus` receives its controller in the `hello` reply instead of claiming argv[1] | not started |
| D3 | The manager claims the seeded devices at boot, before any driver is spawned | **merged into D4** — see below |
| D4 | The manager claims + delegates on `hello`; `usb-xhci-bus` is the first driver converted | **done** — caught an IOMMU regression I introduced; see below |
| D5 | The other four claimants converted: `pci-bus`, `ps2-bus`, `virtio-gpu`, `acpi` | not started |
| D6 | `device_claim` refuses a device the caller was not handed; the hole is closed | not started |
| D7 | Zero-resource devices stop being kernel objects — inventory moves to the manager | not started |
@@ -50,11 +50,36 @@ that cannot safely run in user space.**
| D9 | The device table becomes dynamic; **`maximum_devices` deleted**; per-holder quota declared | not started |
Ordering is load-bearing. D1–D2 build and prove the mechanism with nothing depending on
it. D3–D5 move each claimant across one at a time, so the suite stays green throughout
it. D4–D5 move each claimant across one at a time, so the suite stays green throughout
and a regression names the driver that caused it. D6 is the flag day. D7 must precede
D9, because zero-resource children are the case that sidesteps containment and so the
reason a shared cap was needed at all.
**D3 merged into D4** (found while implementing, recorded rather than worked around).
The two cannot be separated: the moment the manager claims a device, any driver still
calling `device_claim` on it is refused `AlreadyClaimed`, so D3 on its own turns the
suite red — and D3 applied to *nothing* changes no behaviour and cannot be tested.
They land together, with the manager claiming only for drivers in an explicit
**delegated set** so every unconverted driver keeps claiming exactly as before.
`usb-xhci-bus` is the first member, as it was the first driver to conform to `hello`
(device-manager.md, M18.1). D5 moves the rest in one at a time; the set and the
`device_claim` path both disappear at D6.
### Observations from the run
- **D4 introduced an IOMMU regression, caught by converting one driver at a time.**
`confineDevice` runs inside `systemDeviceClaim`, so a device arriving by *transfer*
was never confined for its new owner: the driver's DMA rings went unbound (three
IOMMU+USB cases failed), and two worse consequences were latent — a manager death
would have torn down a domain a live driver was using, and a driver death would have
leaked one. `iommu.reassign` moves the confinement with the device, keeping the
domain and attachment intact so it never translates through nothing. Converting all
five drivers at once would have produced the same three failures with five suspects.
- **`usb-hub` failed once in a full run, then passed six times** (four isolated, two
full). Suspected instance of the known intermittent AP ring-3 fault rather than
anything in D4 — recorded rather than dismissed, because D4 moved the `hello` earlier
and so did shift boot timing. Watch it across the remaining steps.
### Settled, so the run does not re-litigate them
- **The manager claims, it is not granted.** No binary names in the kernel; the rule is