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:
@@ -173,10 +173,22 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
if (!channel.bindPatiently("usb-transfer", endpoint))
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: /protocol/usb-transfer is another controller's; serving mine unnamed\n");
|
||||
|
||||
device.claim(controller_id) catch |e| {
|
||||
std.log.warn("unable to claim controller device {d}: {s}", .{ controller_id, @errorName(e) });
|
||||
// **The handshake comes first, because it is where the device arrives.** This
|
||||
// driver used to claim `controller_id` here — first-come-first-served, so the
|
||||
// manager's matching was advisory and any process could have claimed it by
|
||||
// passing the same integer. Now the manager holds the controller and transfers
|
||||
// it in `onHello`, so by the time this call returns the device is ours and
|
||||
// nothing else could have taken it (docs/os-development/device-authority.md).
|
||||
//
|
||||
// `hello` is synchronous, so the transfer has completed before the reply lands —
|
||||
// there is no window between being told yes and holding the thing.
|
||||
//
|
||||
// Keep the handle: the tick's hot-plug dispatch reports through it.
|
||||
const handle = device_manager.hello(.bus, controller_id) orelse {
|
||||
std.log.warn("no hello with the device manager; controller {d} not delegated", .{controller_id});
|
||||
return false;
|
||||
};
|
||||
manager_handle = handle;
|
||||
|
||||
// Fetch our own descriptor back for the controller's resources.
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
@@ -204,7 +216,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
std.log.info("controller device {d} has no register BAR", .{controller_id});
|
||||
return false;
|
||||
};
|
||||
std.log.info("claimed controller device {d} (registers at 0x{x}, {d} bytes)", .{
|
||||
std.log.info("controller device {d} registers at 0x{x}, {d} bytes", .{
|
||||
controller_id,
|
||||
register_window.start,
|
||||
register_window.len,
|
||||
@@ -247,12 +259,6 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The handshake (role: bus — we enumerate USB ports and report the devices
|
||||
// behind them), inside the manager's hello deadline. Keep the handle: the
|
||||
// tick's hot-plug dispatch reports through it.
|
||||
const handle = device_manager.hello(.bus, controller_id) orelse return false;
|
||||
manager_handle = handle;
|
||||
|
||||
scanPorts(handle);
|
||||
|
||||
// Arm the timer: in polling mode it drains the event ring; in MSI mode it is the
|
||||
|
||||
Reference in New Issue
Block a user