protocols: attach gets its reverse — block detach, usb-transfer dma_detach
The kernel was always symmetric (dma_bind 51 / dma_unbind 52); the two protocols that forward an attachment up the stack were one-way, so a live client could grant a device reach into its buffer but never revoke it while alive — exactly the one-way lifecycle the storage architecture's enforcement section forbids. Death stays the mechanical backstop; detach is the living process's path. Both verbs are appended, so every existing number holds. The shape mirrors attach precisely: the same region capability rides the cap slot again — the kernel matches the region, so no layer retains anything between the calls (the bus never kept the handle; now it never needs to). fat's bring-up does attach -> detach -> attach, exercising both verbs through the whole chain (fat -> storage -> bus -> kernel) on every boot: a broken detach fails every fat case instead of lying dormant until the first buffer replacement. Honest scope: the round trip proves the plumbing; unbind semantics are the kernel iommu tests' (map/unmap/translationOf); the full composition (detach then DMA faults) is a future iommu-fault extension.
This commit is contained in:
@@ -54,6 +54,12 @@ pub const Protocol = envelope.Define(.{
|
||||
// physical addresses (named in later read/write) are reachable by the
|
||||
// device under an enforcing IOMMU. Call once per buffer before using it.
|
||||
.{ .name = "attach" },
|
||||
// detach(): the reverse — the same region capability rides the cap slot
|
||||
// (the caller still holds its handle; the kernel matches the region) and
|
||||
// the buffer leaves the device's domain. Every grant a live process
|
||||
// makes is revocable by the granter while alive; death remains the
|
||||
// mechanical backstop (storage-architecture.md, the lifecycle rule).
|
||||
.{ .name = "detach" },
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -156,6 +156,11 @@ pub const Protocol = envelope.Define(.{
|
||||
// target says which caller's device is attaching, so there is nothing
|
||||
// left for a body to carry.
|
||||
.{ .name = "dma_attach" },
|
||||
// dma_detach: the reverse, same shape — the region capability rides the
|
||||
// cap slot again (the kernel matches the region; the provider retains
|
||||
// nothing between the two calls) and the buffer leaves the controller's
|
||||
// domain. Appended, so every existing verb keeps its number.
|
||||
.{ .name = "dma_detach" },
|
||||
},
|
||||
.events = &.{
|
||||
.{ .name = "interrupt_report", .payload = InterruptReport },
|
||||
@@ -188,6 +193,7 @@ test "the verb numbering, and the device token in the header" {
|
||||
try std.testing.expectEqual(@as(u32, 18), @intFromEnum(Operation.interrupt_subscribe));
|
||||
try std.testing.expectEqual(@as(u32, 19), @intFromEnum(Operation.bulk));
|
||||
try std.testing.expectEqual(@as(u32, 20), @intFromEnum(Operation.dma_attach));
|
||||
try std.testing.expectEqual(@as(u32, 21), @intFromEnum(Operation.dma_detach));
|
||||
try std.testing.expectEqual(@as(u32, 16), @intFromEnum(Event.interrupt_report));
|
||||
|
||||
var buffer: [message_maximum]u8 = undefined;
|
||||
|
||||
Reference in New Issue
Block a user