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:
@@ -593,6 +593,7 @@ const handlers = Serve.Handlers{
|
||||
.interrupt_subscribe = onInterruptSubscribe,
|
||||
.bulk = onBulk,
|
||||
.dma_attach = onDmaAttach,
|
||||
.dma_detach = onDmaDetach,
|
||||
};
|
||||
|
||||
/// open: the target is the class driver's assigned device id. Resolve it to an
|
||||
@@ -694,6 +695,14 @@ fn onDmaAttach(_: void, invocation: Invocation(void), _: Answer(void)) isize {
|
||||
return if (device.dmaBind(controller_id, handle)) 0 else refused;
|
||||
}
|
||||
|
||||
/// The reverse: the same region capability arrives again and the buffer leaves
|
||||
/// the controller's domain (`dma_unbind` matches the region — nothing was
|
||||
/// retained here between the two calls). The turn closes the arriving copy.
|
||||
fn onDmaDetach(_: void, invocation: Invocation(void), _: Answer(void)) isize {
|
||||
const handle = invocation.capability orelse return -envelope.EPROTO;
|
||||
return if (device.dmaUnbind(controller_id, handle)) 0 else refused;
|
||||
}
|
||||
|
||||
/// A timer tick or an MSI landed: drain the event ring, reconcile ports, and fan out.
|
||||
/// The timer arm re-arms itself (8 ms drain when polling, 250 ms reconcile under MSI);
|
||||
/// the MSI arm clears the interrupter's pending bit FIRST, then drains — so an event
|
||||
|
||||
Reference in New Issue
Block a user